Lucene基础(2)

上一篇:Lucene基础(1) 

一、Lucene术语

Document, Field, Term, Query, Analyzer相信在其中大多数在之前已经理解了...对其中部分概念详细说明

Document是一个包含了多个Field的容器,通过以下代码应该容易理解二者的关系

  Document document=new Document();
        //Field.Store.YES或者NO(存储域选项)
        //设置为YES表示或把这个域中的内容完全存储到文件中,方便进行文本的还原
        //设置为NO表示把这个域的内容不存储到文件中,但是可以被索引,此时内容无法完全还原(doc.get)
        for(int i=0;i<ids.length;i++){
            document.add(new StringField("ids",ids[i], Field.Store.YES));
            document.add(new StringField("names",names[i], Field.Store.YES));
            document.add(new TextField("describes",describes[i], Field.Store.YES));
            indexWriter.addDocument(document);
        }

Field的一般构造器:

protected Field(String name, FieldType type)

下面说明常见的FieldType

http://lucene.apache.org/core/5_5_3/core/org/apache/lucene/document/FieldType.html

(1) NumbericType

public enum NumericType {
    /** 32-bit integer numeric type */
    INT, 
    /** 64-bit long numeric type */
    LONG, 
    /** 32-bit float numeric type */
    FLOAT, 
    /** 64-bit double numeric type */
    DOUBLE
  }

如果是数值类型,可以通过NumbericType来指明

(2) Stored

private boolean stored;

是否存储field的值。如果true,原始的字符串值全部被保存在索引中,并可以由IndexReader类恢复。该选项对于需要展示搜索结果的一些域很有用(如URL,标题等)。如果为false,则索引中不存储field的值,通常用来索引大的文本域值。如Web页面的正文

(3) tokenized

 private boolean tokenized = true;

是否使用分析器将域值分解成独立的语汇单元流。该属性仅当indexed()为true时有效.

(4) 加权相关

  • private boolean storeTermVectors;当lucene建立起倒排索引后,默认情况下它会保存所有必要的信息实施Vector Space Model。该Model需要计算文档中出现的term数,以及他们出现的位置。该属性仅当indexed为true时生效。他会为field建立一个小型的倒排索引。
  • private boolean storeTermVectorOffsets;表示是否存储field的token character的偏移量到 term vectors向量中。
  • private boolean storeTermVectorPositions;表示是否存储field中token的位置到term vectors 向量中。
  • private boolean storeTermVectorPayloads;是否存储field中token的比重到term vectors中。
  • private boolean omitNorms;是否要忽略field的加权基准值,如果为true可以节省内存消耗,但在打分质量方面会有更高的消耗,另外你也不能使用index-time 进行加权操作。

(5) IndexOptions

// NOTE: order is important here; FieldInfo uses this
  // order to merge two conflicting IndexOptions (always
  // "downgrades" by picking the lowest).
  /** Not indexed */
  NONE,
  /** 
   * Only documents are indexed: term frequencies and positions are omitted.
   * Phrase and other positional queries on the field will throw an exception, and scoring
   * will behave as if any term in the document appears only once.
   */
  DOCS,
  /** 
   * Only documents and term frequencies are indexed: positions are omitted. 
   * This enables normal scoring, except Phrase and other positional queries
   * will throw an exception.
   */  
  DOCS_AND_FREQS,
  /** 
   * Indexes documents, frequencies and positions.
   * This is a typical default for full-text search: full scoring is enabled
   * and positional queries are supported.
   */
  DOCS_AND_FREQS_AND_POSITIONS,
  /** 
   * Indexes documents, frequencies, positions and offsets.
   * Character offsets are encoded alongside the positions. 
   */
  DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS,

说明如下:

  • DOCS_ONLY:仅documents被索引,term的频率和位置都将被忽略。针对field的短语或有关位置的查询都将抛出异常。 
  • DOCS_AND_FREQS:documents和term的频率被索引,term的位置被忽略。这样可以正常打分,但针对field的短语或有关位置的查询都将抛出异常。 
  • DOCS_AND_FREQS_AND_POSITIONS:这是一个全文检索的默认设置,打分和位置检索都支持。 
  • DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS:索引字符相对位置的偏移量。

(6) DocValueType

/**
   * No doc values for this field.
   */
  NONE,
  /** 
   * A per-document Number
   */
  NUMERIC,
  /**
   * A per-document byte[].  Values may be larger than
   * 32766 bytes, but different codecs may enforce their own limits.
   */
  BINARY,
  /** 
   * A pre-sorted byte[]. Fields with this type only store distinct byte values 
   * and store an additional offset pointer per document to dereference the shared 
   * byte[]. The stored byte[] is presorted and allows access via document id, 
   * ordinal and by-value.  Values must be {@code <= 32766} bytes.
   */
  SORTED,
  /** 
   * A pre-sorted Number[]. Fields with this type store numeric values in sorted
   * order according to {@link Long#compare(long, long)}.
   */
  SORTED_NUMERIC,
  /** 
   * A pre-sorted Set&lt;byte[]&gt;. Fields with this type only store distinct byte values 
   * and store additional offset pointers per document to dereference the shared 
   * byte[]s. The stored byte[] is presorted and allows access via document id, 
   * ordinal and by-value.  Values must be {@code <= 32766} bytes.
   */
  SORTED_SET,

如果非空,field的值将被索引成docValues. 

  • NUMERIC:数字类型 
  • BINARY:二进制类型 
  • SORTED:只保存不同的二进制值 byte[] 
  • SORTED_SET.

(7) frozen

阻止field属性未来可能的变更,该属性通常在FieldType 属性已经被设置后调用。是为了防止无意识的变更

 

二、增删改查演示

原文链接:http://www.kailing.pub/index/columns/colid/16.html

 

public class IndexerCRUD {
    //测试数据,模拟数据库表结构
    private static String[] ids={"1","2","3"}; //用户ID
    private static String [] names={"kl","wn","sb"};
    private static String [] describes={"shi yi ge mei nan zi","Don't know","Is an idiot\n"};
    //索引存储地址
    private static String indexDir="G:\\projects-helloworld\\lucene\\src\\main\\resources\\LuceneIndex";
    /**
     * 获取操作索引实体,并添加测试数据
     * @param indexDir 索引存储位置
     * @return
     * @throws Exception
     */
    public static IndexWriter getIndexWriter(String indexDir)throws Exception{
        IndexWriterConfig writerConfig=new IndexWriterConfig(getAnalyzer());
        IndexWriter indexWriter=new IndexWriter(getDirectory(indexDir),writerConfig);
        Document document=new Document();
        //Field.Store.YES或者NO(存储域选项)
        //设置为YES表示或把这个域中的内容完全存储到文件中,方便进行文本的还原
        //设置为NO表示把这个域的内容不存储到文件中,但是可以被索引,此时内容无法完全还原(doc.get)
        for(int i=0;i<ids.length;i++){
            document.add(new StringField("ids",ids[i], Field.Store.YES));
            document.add(new StringField("names",names[i], Field.Store.YES));
            document.add(new TextField("describes",describes[i], Field.Store.YES));
            indexWriter.addDocument(document);
        }
        return indexWriter;
    }
    /**
     * 得到默认分词器
     * @return
     */
    public static Analyzer getAnalyzer(){
        return  new StandardAnalyzer();
    }
    /**
     * 得到索引磁盘存储器
     * @param indexDir 存储位置
     * @return
     */
    public static Directory getDirectory(String indexDir){
        Directory directory=null;
        try {
            directory= FSDirectory.open(Paths.get(indexDir));
        }catch (Exception e){
            e.printStackTrace();
        }
        return  directory;
    }
    /**
     * 获取读索引实体,并打印读到的索引信息
     * @return
     */
    public  static IndexReader getIndexReader(){
        IndexReader reader=null;
        try {
            reader= DirectoryReader.open(getDirectory(indexDir));
            //通过reader可以有效的获取到文档的数量
            System.out.println("当前存储的文档数::"+reader.numDocs());
            System.out.println("当前存储的文档数,包含回收站的文档::"+reader.maxDoc());
            System.out.println("回收站的文档数:"+reader.numDeletedDocs());

        } catch (Exception e) {
            e.printStackTrace();
        }
        return reader;
    }


    /**
     * 写索引测试,借助Luke观察结果
     * @throws Exception
     */
    @Test
    public void Testinsert() throws  Exception{
        IndexWriter writer=getIndexWriter(indexDir);
        writer.close();
        getIndexReader();
    }
    /**
     * 删除索引测试,借助Luke观察结果
     * @throws Exception
     */
    public void TestDelete()throws Exception{
        //测试删除前我们先把上次的索引文件删掉,或者换个目录
        IndexWriter writer=getIndexWriter(indexDir);
        QueryParser parser=new QueryParser("ids", getAnalyzer());//指定Document的某个属性
        Query query=parser.parse("2");//指定索引内容,对应某个分词
        Term term=new Term("names","kl");
        //参数是一个选项,可以是一个query,也可以是一个term,term是一个精确查找的值
        writer.deleteDocuments(query);//此时删除的文档并不会被完全删除,而是存储在一个回收站中的,可以恢复
        writer.forceMergeDeletes();//强制合并删除的索引信息,索引量大的时候不推荐使用,真正的删除
        // writer.commit(); //更改索引要提交,和提交数据库事务一个概念,真正的删除
        writer.close();
        getIndexReader();
    }
    /**
     * 更新操作测试,借助Luke观察结果
     * @throws Exception
     */
    public void TestUpdate()throws Exception{
        // Lucene并没有提供更新,这里的更新操作相当于新增,他并不会去掉原来的信息
        IndexWriter writer = getIndexWriter(indexDir);
        try {
            Document doc = new Document();
            doc.add(new StringField("id","1",Field.Store.YES));
            doc.add(new StringField("names","ckl",Field.Store.YES));
            doc.add(new StringField("describes","chenkailing",Field.Store.NO));
            writer.updateDocument(new Term("id","1"), doc);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(writer!=null) writer.close();
        }
    }
    /**
     * 查询测试
     */
    @Test
    public void TestSearchaer(){
        try {
            IndexReader reader = getIndexReader();
            IndexSearcher searcher = new IndexSearcher(reader);
            QueryParser parser=new QueryParser("names", getAnalyzer());//指定Document的某个属性
            Query query=parser.parse("kl");//指定索引内容,对应某个分词
            Term term=new Term("names","kl");
            //参数是一个选项,可以是一个query,也可以是一个term,term是一个精确查找的值
            TopDocs hits = searcher.search(query, 10);
            for(ScoreDoc sd:hits.scoreDocs) {
                Document doc = searcher.doc(sd.doc);
                System.out.println(
                        doc.get("names")+"["+doc.get("describes")+"]-->"+doc.get("ids"));
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 

 

 

 三、常见Query用法

 

Query 是一个用于查询的抽象基类。搜索指定单词或词组涉及到在项中包装它们,将项添加到查询对象,将查询对象传递到 IndexSearcher 的搜索方法。

 

Lucene 包含各种类型的具体查询实现,比如 TermQuery、BooleanQuery、PhraseQuery、PrefixQuery、RangeQuery、MultiTermQuery、FilteredQuery、SpanQuery 等。以下部分讨论 Lucene 查询 API 的主查询类。

Basic: QueryParser和ScoreDoc

QueryParser 对于解析人工输入的查询字符非常有用。您可以使用它将用户输入的查询表达式解析为 Lucene 查询对象,这些对象可以传递到 IndexSearcher 的搜索方法。它可以解析丰富的查询表达式。 QueryParser 内部将人们输入的查询字符串转换为一个具体的查询子类。您需要使用反斜杠(\)将 *、? 等特殊字符进行转义。您可以使用运算符 AND、OR 和 NOT 构建文本布尔值查询。

QueryParser queryParser = new QueryParser("subject",new StandardAnalyzer());
// Search for emails that contain the words 'job openings' and '.net' and 'pune'
Query query = queryParser.parse("job openings AND .net AND pune");

indexSearcher 返回一组对分级搜索结果(如匹配给定查询的文档)的引用。您可以使用 IndexSearcher 的搜索方法确定需要检索的最优先搜索结果数量。可以在此基础上构建定制分页。您可以添加定制 Web 应用程序或桌面应用程序来显示搜索结果。检索搜索结果涉及的主要类包括 ScoreDoc 和 TopDocs。

ScoreDoc: 搜索结果中包含一个指向文档的简单指针。这可以封装文档索引中文档的位置以及 Lucene 计算的分数。 
封装搜索结果以及 ScoreDoc 的总数。 
以下代码片段展示了如何检索搜索结果中包含的文档。

/* First parameter is the query to be executed and 
second parameter indicates the no of search results to fetch */ 
TopDocs topDocs = indexSearcher.search(query,20); 
System.out.println(“Total hits “+topDocs.totalHits);

// Get an array of references to matched documents 
ScoreDoc[] scoreDosArray = topDocs.scoreDocs; 
for(ScoreDoc scoredoc: scoreDosArray){ 
//Retrieve the matched document and show relevant details 
  Document doc = indexSearcher.doc(scoredoc.doc); 
  System.out.println(“\nSender: “+doc.getField(“sender”).stringValue()); 
  System.out.println(“Subject: “+doc.getField(“subject”).stringValue()); 
  System.out.println(“Email file location: ” +doc.getField(“emailDoc”).stringValue()); 
}

 

3.1 TermQuery

搜索索引最基本的查询类型。可以使用单个项构建TermQuery。项值应该区分大小写,但也并非全是如此。注意,传递的搜索项应该与文档分析得到的项一致,因为分析程序在构建索引之前对原文本执行许多操作。 
例如,考虑电子邮件标题 “Job openings for Java Professionals at Bangalore”。假设您使用 StandardAnalyzer 编制索引。现在如果我们使用 TermQuery 搜索 “Java”,它不会返回任何内容,因为本文本应该已经规范化,并通过 StandardAnalyzer 转成小写。如果搜索小写单词 “java”,它将返回所有标题字段中包含该单词的邮件。

 

//Search mails having the word "java" in the subject field
Searcher indexSearcher = new IndexSearcher(indexDirectory);
Term term = new Term("subject","java");
Query termQuery = new TermQuery(term);
TopDocs topDocs = indexSearcher.search(termQuery,10);

 

3.2 RangeQuery

您可以使用 RangeQuery 在某个范围内搜索。索引中的所有项都以字典顺序排列。Lucene 的 RangeQuery 允许用户在某个范围内搜索项。该范围可以使用起始项和最终项(包含两端或不包含两端均可)指定。

 

/* RangeQuery example:Search mails from 01/06/2009 to 6/06/2009
both inclusive */
Term begin = new Term("date","20090601");
Term end = new Term("date","20090606");
Query query = new RangeQuery(begin, end, true);

 

3.3 PrefixQuery

您可以使用 PrefixQuery 通过前缀单词进行搜索,该方法用于构建一个查询,该查询查找包含以指定单词前缀开始的词汇的文档。

//Search mails having sender field prefixed by the word 'job'
PrefixQuery prefixQuery = new PrefixQuery(new Term("sender","job"));
PrefixQuery query = new PrefixQuery(new Term("sender","job"));

 

3.4 BooleanQuery

您可以使用 BooleanQuery 组合任何数量的查询对象,构建强大的查询。它使用 query 和一个关联查询的子句,指示查询是应该发生、必须发生还是不得发生。在 BooleanQuery 中,子句的最大数量默认限制为 1,024。您可以调用 setMaxClauseCount 方法设置最大子句数。

// Search mails have both 'java' and 'bangalore' in the subject field
Query query1 = new TermQuery(new Term("subject","java"));
Query query2 = new TermQuery(new Term("subject","bangalore"));
BooleanQuery query = new BooleanQuery();
query.add(query1,BooleanClause.Occur.MUST);
query.add(query2,BooleanClause.Occur.MUST);

 

3.5 PhraseQuery

您可以使用 PhraseQuery 进行短语搜索。PhraseQuery 匹配包含特定单词序列的文档。PhraseQuery 使用索引中存储的项的位置信息。考虑匹配的项之间的距离称为 slop。默认情况下,slop 的值为零,这可以通过调用 setSlop 方法进行设置。PhraseQuery 还支持多个项短语。

/* PhraseQuery example: Search mails that have phrase 'job opening j2ee'
   in the subject field.*/
PhraseQuery query = new PhraseQuery();
query.setSlop(1);
query.add(new Term("subject","job"));
query.add(new Term("subject","opening"));
query.add(new Term("subject","j2ee"));

 

3.6 WildcardQuery

WildcardQuery 实现通配符搜索查询,这允许您搜索 arch*(可以查找包含 architect、architecture 等)之类的单词。使用两个标准通配符: 
* 表示零个以上 
? 表示一个以上 
如果使用以通配符查询开始的模式进行搜索,则可能会引起性能的降低,因为这需要查询索引中的所有项以查找匹配文档。

//Search for 'arch*' to find e-mail messages that have word 'architect' in the subject
field./
Query query = new WildcardQuery(new Term("subject","arch*"));

 

3.7 FuzzyQuery

您可以使用 FuzzyQuery 搜索类似项,该类匹配类似于指定单词的单词。类似度测量基于 Levenshtein(编辑距离)算法进行。在列表 9 中,FuzzyQuery 用于查找与拼错的单词 “admnistrtor” 最接近的项,尽管这个错误单词没有索引。

/* Search for emails that have word similar to 'admnistrtor' in the
subject field. Note we have misspelled admnistrtor here.*/
Query query = new FuzzyQuery(new Term("subject", "admnistrtor"));

 

posted @ 2016-11-02 11:27  carl_ysz  阅读(411)  评论(0编辑  收藏  举报