Lucene Field域,索引&搜索

来源:互联网 发布:java页面跳转代码 编辑:程序博客网 时间:2024/06/17 13:41

Field

1.1 Field属性

Field是文档中的域,包括Field名和Field值两部分,一个文档可以包括多个FieldDocument只是Field的一个承载体,Field值即为要索引的内容,也是要搜索的内容。 

是否分词(tokenized)

是:作分词处理,即将Field值进行分词,分词的目的是为了索引。

比如:商品名称、商品简介等,这些内容用户要输入关键字搜索,由于搜索的内容格式大、内容多需要分词后将语汇单元索引。

 否:不作分词处理

 比如:商品id、订单号、身份证号等

是否索引(indexed)

是:进行索引。将Field分词后的词或整个Field值进行索引,索引的目的是为了搜索。

比如:商品名称、商品简介分析后进行索引,订单号、身份证号不用分析但也要索引,这些将来都要作为查询条件。

 否:不索引。该域的内容无法搜索到

 比如:商品id、文件路径、图片路径等,不用作为查询条件的不用索引。

 l 是否存储(stored)

是:将Field值存储在文档中,存储在文档中的Field才可以从Document中获取。

 比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。

 否:不存储Field值,不存储的Field无法通过Document获取

 比如:商品简介,内容较大不用存储。如果要向用户展示商品简介可以从系统的关系数据库中获取商品简介。

1.2 Field常用类型

下边列出了开发中常用 Filed类型,注意Field的属性,根据需求选择:

 

Field

数据类型

Analyzed

是否分词

Indexed

是否索引

Stored

是否存储

说明

StringField(FieldName, FieldValue,Store.YES))

字符串

N

Y

YN

这个Field用来构建一个字符串Field,但是不会进行分词,会将整个串存储在索引中,比如(订单号,身份证号等)

是否存储在文档中用Store.YESStore.NO决定

LongField(FieldName, FieldValue,Store.YES)

Long

Y

Y

YN

这个Field用来构建一个Long数字型Field,进行分词和索引,比如(价格)

是否存储在文档中用Store.YESStore.NO决定

StoredField(FieldName, FieldValue) 

重载方法,支持多种类型

N

N

Y

这个Field用来构建不同类型Field

不分析,不索引,但要Field存储在文档中

TextField(FieldName, FieldValue, Store.NO)

TextField(FieldName, reader)

 

字符串

Y

Y

YN

如果是一个Reader, lucene猜测内容比较多,会采用Unstored的策略.

 

 1.3 Field代码修改如下

图书id

是否分词:不用分词,因为不会根据商品id来搜索商品 

是否索引:不索引,因为不需要根据图书ID进行搜索

是否存储:要存储,因为查询结果页面需要使用id这个值。

 图书名称:

是否分词:要分词,因为要将图书的名称内容分词索引,根据关键搜索图书名称抽取的词。

是否索引:要索引。

是否存储:要存储。

 图书价格:

是否分词:要分词,lucene对数字型的值只要有搜索需求的都要分词和索引,因为lucene对数字型的内容要特殊分词处理,本例子可能要根据价格范围搜索,需要分词和索引。

 是否索引:要索引

 是否存储:要存储

 图书图片地址:

是否分词:不分词

是否索引:不索引

是否存储:要存储

 图书描述:

是否分词:要分词

是否索引:要索引

是否存储:因为图书描述内容量大,不在查询结果页面直接显示,不存储。

 不存储是来不在lucene的索引文件中记录,节省lucene的索引文件空间,如果要在详情页面显示描述,思路:

lucene中取出图书的id,根据图书的id查询关系数据库中book表得到描述信息。

 

// 图书ID

// 参数:域名、域中存储的内容、是否存储

// 不分词、索引、要存储

// Field id = new TextField("id", book.getId().toString(),

// Store.YES);

Field id = new StoredField("id", book.getId().toString(),Store.YES);

// 图书名称

// 分词、索引、存储

Field bookname = new TextField("bookname", book.getName(),

Store.YES);

// 图书价格

// 分词、索引、存储

Field price = new FloatField("price", book.getPrice(),Store.YES);

// 图书图片

// 不分词、不索引、要存储

Field pic = new StoredField("pic", book.getPic());

// 图书描述

// 分词、索引、不存储

Field description = new TextField("description",

book.getDescription(), Store.NO);

 

 2 索引维护

2.1 需求

管理人员通过电商系统更改图书信息,这时更新的是数据库,如果使用lucene搜索图书信息需要在数据库表book信息变化时及时更新lucene索引库。

2.2 添加索引

调用 indexWriter.addDocumentdoc)添加索引。

参考创建索引。

2.3 删除索引

2.3.1 删除指定索引

根据Term删除索引,满足条件的将全部删除。

// 删除索引

@Test

public void deleteIndex()throws Exception {

// 1、指定索引库目录

Directory directory = FSDirectory.open(new File("E:\\11-index\\0720"));

// 2、创建IndexWriterConfig

IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST,

new StandardAnalyzer());

// 3、 创建IndexWriter

IndexWriter writer = new IndexWriter(directory, cfg);

// 4、通过IndexWriter来删除索引

// b)、删除指定索引

writer.deleteDocuments(new Term("filename","apache"));

// 5、关闭IndexWriter

writer.close();

}

 

 2.3.2 删除全部索引(慎用)

将索引目录的索引信息全部删除,直接彻底删除,无法恢复。慎用!!!

// 删除索引

@Test

public void deleteIndex()throws Exception {

// 1、指定索引库目录

Directory directory = FSDirectory.open(new File("E:\\11-index\\0720"));

// 2、创建IndexWriterConfig

IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST,

new StandardAnalyzer());

// 3、 创建IndexWriter

IndexWriter writer = new IndexWriter(directory, cfg);

// 4、通过IndexWriter来删除索引

// a)、删除全部索引

writer.deleteAll();

// 5、关闭IndexWriter

writer.close();

}

 

建议参照关系数据库基于主键删除方式,所以在创建索引时需要创建一个主键Field,删除时根据此主键Field删除。

索引删除后将放在Lucene的回收站中,Lucene3.X版本可以恢复删除的文档,3.X之后无法恢复。

2.4 修改索引

更新索引是先删除再添加,建议对更新需求采用此方法并且要保证对已存在的索引执行更新,可以先查询出来,确定更新记录存在执行更新操作。

// 修改索引

@Test

public void updateIndex()throws Exception {

// 1、指定索引库目录

Directory directory = FSDirectory.open(new File("E:\\11-index\\0720"));

// 2、创建IndexWriterConfig

IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST,

new StandardAnalyzer());

// 3、 创建IndexWriter

IndexWriter writer = new IndexWriter(directory, cfg);

// 4、通过IndexWriter来修改索引

// a)、创建修改后的文档对象

Document document = new Document();

 

// 文件名称

Field filenameField = new StringField("filename","updateIndex", Store.YES);

document.add(filenameField);

 

// 修改指定索引为新的索引

writer.updateDocument(new Term("filename","apache"), document);

 

// 5、关闭IndexWriter

writer.close();

}

 

搜索

3.1 创建查询的两种方法

对要搜索的信息创建Query查询对象,Lucene会根据Query查询对象生成最终的查询语法。类似关系数据库Sql语法一样,Lucene也有自己的查询语法,比如:“name:lucene”表示查询Fieldname为“lucene”的文档信息。

可通过两种方法创建查询对象:

1)使用Lucene提供Query子类

Query是一个抽象类,lucene提供了很多查询对象,比如TermQuery项精确查询,NumericRangeQuery数字范围查询等。

如下代码:

Query query = new TermQuery(new Term("name","lucene"));

 

2)使用QueryParse解析查询表达式

QueryParser会将用户输入的查询表达式解析成Query对象实例。

如下代码:

QueryParser queryParser = new QueryParser("name",new IKAnalyzer());

Query query = queryParser.parse("name:lucene");

 

 3.2 通过Query子类搜索

3.2.1 TermQuery

TermQuery项查询,TermQuery不使用分析器,搜索关键词作为整体来匹配Field域中的词进行查询,比如订单号、分类ID号等。

private void doSearch(Query query) {

IndexReader reader = null;

try {

// a) 指定索引库目录

Directory indexdirectory = FSDirectory.open(new File(

"E:\\11-index\\0720"));

// b) 创建IndexReader对象

reader = DirectoryReader.open(indexdirectory);

// c) 创建IndexSearcher对象

IndexSearcher searcher = new IndexSearcher(reader);

// d) 通过IndexSearcher对象执行查询索引库,返回TopDocs对象

// 第一个参数:查询对象

// 第二个参数:最大的n条记录

TopDocs topDocs = searcher.search(query, 10);

// e) 提取TopDocs对象中的文档ID,如何找出对应的文档

ScoreDoc[] scoreDocs = topDocs.scoreDocs;

System.out.println("总共查询出的结果总数为:" + topDocs.totalHits);

Document doc;

for (ScoreDoc scoreDoc : scoreDocs) {

// 文档对象ID

int docId = scoreDoc.doc;

doc = searcher.doc(docId);

// f) 输出文档内容

System.out.println(doc.get("filename"));

System.out.println(doc.get("path"));

System.out.println(doc.get("size"));

}

} catch (IOException e) {

e.printStackTrace();

} finally {

if (reader != null) {

try {

reader.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

 

@Test

public void testTermQuery()throws Exception {

// 1、 创建查询(Query对象)

Query query = new TermQuery(new Term("filename","apache"));

// 2、 执行搜索

doSearch(query);

}

 

3.2.2 NumbericRangeQuery

NumericRangeQuery,指定数字范围查询.

 

@Test

public void testNumbericRangeQuery()throws Exception {

// 创建查询

// 第一个参数:域名

// 第二个参数:最小值

// 第三个参数:最大值

// 第四个参数:是否包含最小值

// 第五个参数:是否包含最大值

Query query = NumericRangeQuery.newLongRange("size", 1l, 100l,true,true);

// 2、 执行搜索

doSearch(query);

}

 

 

3.2.3 BooleanQuery

BooleanQuery,布尔查询,实现组合条件查询。

@Test

public void booleanQuery()throws Exception {

BooleanQuery query = new BooleanQuery();

Query query1 = new TermQuery(new Term("id","3"));

Query query2 = NumericRangeQuery.newFloatRange("price", 10f, 200f,

true, true);

 

//MUST:查询条件必须满足,相当于AND

//SHOULD:查询条件可选,相当于OR

//MUST_NOT:查询条件不能满足,相当于NOT

query.add(query1, Occur.MUST);

query.add(query2, Occur.SHOULD);

System.out.println(query);

 

search(query);

}

 

组合关系代表的意思如下:

     1MUSTMUST表示“与”的关系,即“并集”。

     2MUSTMUST_NOT前者包含后者不包含。

     3MUST_NOTMUST_NOT没意义

     4SHOULDMUST表示MUSTSHOULD失去意义;

     5SHOUlDMUST_NOT相当于MUSTMUST_NOT

     6SHOULDSHOULD表示“或”的概念。

3.3 通过QueryParser搜索

通过QueryParser也可以创建QueryQueryParser提供一个Parse方法,此方法可以直接根据查询语法来查询。Query对象执行的查询语法可通过System.out.println(query);查询。

3.3.1 QueryParser

3.3.1.1 代码实现

@Test

public void testQueryParser()throws Exception {

// 创建QueryParser

// 第一个参数:默认域名

// 第二个参数:分词器

QueryParser queryParser = new QueryParser("name",new IKAnalyzer());

// 指定查询语法 ,如果不指定域,就搜索默认的域

Query query = queryParser.parse("lucene");

System.out.println(query);

// 2、 执行搜索

doSearch(query);

 

}

3.3.1.2 查询语法

1、基础的查询语法,关键词查询:

域名+“:”+搜索的关键字

例如:content:java

2、范围查询

域名+:+[最小值TO 最大值]

例如:size:[1 TO 1000]

注意:QueryParser不支持对数字范围的搜索,它支持字符串范围。数字范围搜索建议使用NumericRangeQuery

3、组合条件查询

Occur.MUST 查询条件必须满足,相当于and

+(加号)

Occur.SHOULD 查询条件可选,相当于or

 

空(不用符号)

Occur.MUST_NOT 查询条件不能满足,相当于not非

-(减号)

 

1+条件1 +条件2:两个条件之间是并且的关系and

例如:+filename:apache +content:apache

2)+条件1 条件2:必须满足第一个条件,忽略第二个条件

例如:+filename:apache content:apache

3)条件1 条件2:两个条件满足其一即可。

例如:filename:apache content:apache

4-条件1条件2:必须不满足条件1,要满足条件2

例如:-filename:apache content:apache

 

第二种写法:

条件1 AND 条件2

条件1 OR 条件2

条件1 NOT 条件2

 

3.3.2 MultiFieldQueryParser

通过MuliFieldQueryParse对多个域查询。

@Test

public void testMultiFieldQueryParser()throws Exception {

// 可以指定默认搜索的域是多个

String[] fields = { "name","description" };

// 创建一个MulitFiledQueryParser对象

QueryParser parser = new MultiFieldQueryParser(fields,new IKAnalyzer());

// 指定查询语法 ,如果不指定域,就搜索默认的域

Query query = parser.parse("lucene");

// 2、 执行搜索

doSearch(query);

}

 

3.4 TopDocs

 Lucene搜索结果可通过TopDocs遍历,TopDocs类提供了少量的属性,如下:


方法或属性

说明

totalHits

匹配搜索条件的总记录数

 

 

 

 

 

 

scoreDocs

顶部匹配记录

 

注意:

Search方法需要指定匹配记录数量nindexSearcher.search(query, n)

TopDocs.totalHits:是匹配索引库中所有记录的数量

TopDocs.scoreDocs:匹配相关度高的前边记录数组,scoreDocs的长度小于等于search方法指定的参数n