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<byte[]>. 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"));