lucene fieldcache怎么判断匹配到的域 field

lucene搜索输入一个关键字,如何匹配多个索引field索引代码如下
  Document&doc&=&new&Document();
&&&&&&&&doc.add(new&Field("contents",&rs.getString("title"),&Field.Store.YES,
&&&&&&&&Field.Index.ANALYZED));
&&&&&&&&doc.add(new&Field("roomid",&rs.getString("roomid"),&Field.Store.YES,
&&&&&&&&Field.Index.ANALYZED));
&&&&&&&&doc.add(new&Field("address",&rs.getString("address"),&Field.Store.YES,
&&&&&&&&Field.Index.ANALYZED));
&&&&&&&&doc.add(new&Field("cell",&rs.getString("cell"),&Field.Store.YES,
&&&&&&&&Field.Index.ANALYZED));
就是通过一个关键字搜索contents,roomid,address,cell这四个域呢
&下面只是搜索contents//&搜索条件
&&&&&&&&&&&&QueryParser&parser&=&new&QueryParser(Version.LUCENE_43,&"contents",&new&IKAnalyzer(true)&);
&&&&&&&&&&&&Query&query&=&parser.parse(s);
回答1:  BooleanQuery&query&=&new&BooleanQuery();
&&&&&&&&query.add(new&QueryParser(Version.LUCENE_43,&"contents",&new&IKAnalyzer(true)).parse(s),&BooleanClause.Occur.SHOULD);
&&&&&&&&query.add(new&QueryParser(Version.LUCENE_43,&"roomid",&new&IKAnalyzer(true)).parse(s),&BooleanClause.Occur.SHOULD);
&&&&&&&&query.add(new&QueryParser(Version.LUCENE_43,&"address",&new&IKAnalyzer(true)).parse(s),&BooleanClause.Occur.SHOULD);
&&&&&&&&query.add(new&QueryParser(Version.LUCENE_43,&"cell",&new&IKAnalyzer(true)).parse(s),&BooleanClause.Occur.SHOULD);Lucene5学习之Suggest关键字提示 - 嘿↗你的益达 - ITeye技术网站
博客分类:
首先需要搞清楚Suggest模块是用来解决什么问题的?Google我想大家都用过,当我们在搜索输入框里输入搜索关键字的时候,紧贴着输入框下方会弹出一个提示框,提示框里会列出Top N个包含当前用户输入的搜索关键字的搜索热词,如图:
这里说的不是前端的这种JS效果,而说的是输入一个关键字如何获取相关的搜索热词,至于js效果,自己Google JQuery自动补全插件,我以前玩过,这里关注的是提示数据如何获取,当然你也可以使用数据库SQL like "%xxxx%"来实现(xxxx是你输入的搜索关键字),但Lucene来实现这个功能会更好,因为我们都知道Lucene的查询结果是可以根据相关度排序的,支持各种强大的Query查询,这是数据库SQL语法所不能实现的。在Lucene中,这种搜索关键字自动提示功能是由Suggest模块提供的。
要实现搜索关键字提示,首先你需要创建索引,此时创建索引就不是简简单单的借助IndexWrtier.addDocument了,而是需要通过Suggest模块下的AnalyzingInfixSuggester类去build,翻看AnalyzingInfixSuggester类的源码一探究竟,先看看其成员变量声明部分:
public class AnalyzingInfixSuggester extends Lookup implements Closeable {
/** Field name used for the indexed text. */
protected final static String TEXT_FIELD_NAME = "text";
/** Field name used for the indexed text, as a
StringField, for exact lookup. */
protected final static String EXACT_TEXT_FIELD_NAME = "exacttext";
/** Field name used for the indexed context, as a
StringField and a SortedSetDVField, for filtering. */
protected final static String CONTEXTS_FIELD_NAME = "contexts";
/** Analyzer used at search time */
protected final Analyzer queryA
/** Analyzer used at index time */
protected final Analyzer indexA
final Version matchV
private final D
final int minPrefixC
private final boolean allTermsR
private fin
private final boolean commitOnB
/** Used for ongoing NRT additions/updates. */
private IndexW
/** {@link IndexSearcher} used for lookups. */
protected SearcherManager searcherM
/** Default minimum number of leading characters before
PrefixQuery is used (4). */
public static final int DEFAULT_MIN_PREFIX_CHARS = 4;
/** Default boolean clause option for multiple terms matching (all terms required). */
public static final boolean DEFAULT_ALL_TERMS_REQUIRED =
/** Default higlighting option. */
public static final boolean DEFAULT_HIGHLIGHT =
/** How we sort the postings and search results. */
private static final Sort SORT = new Sort(new SortField("weight", SortField.Type.LONG, true));
TEXT_FIELD_NAME:表示搜索关键字域,即我们用户输入的搜索关键字是在这个域上进行匹配的,这个域使用的是TextField且Store.YES,
EXACT_TEXT_FIELD_NAME:它跟TEXT_FIELD_NAME类似,唯一区别就是它使用的是StringFeild且Store.NO,不要问我为什么知道
CONTEXTS_FIELD_NAME:这个域名其实也是用来过滤的,只是它是比较次要的过滤条件域,举个例子吧,比如你有title和content两个域,title表示新闻标题,content表示新闻内容,那这里的CONTEXTS_FIELD_NAME表示的就是content域的域名,一般都是在title域里去过滤,content属于2次过滤或者说是次要级别的过滤,不知道这样说够明确不?
然后是两个分词器,分别对应查询时和创建索引时,两个分词器最好是保持一致,final Version matchV这个就不用说了,Directory指的是索引目录,这个也不用多说大家都懂。minPrefixChars表示最小前缀字符长度,意思就是用户最少输入多少个字符我才开始搜索相关热词,设置这个值是为了避免用户输入字符过短导致返回的匹配结果太多影响性能,比如用户输入一个字符,然后程序就屁颠屁颠的去search,因为条件太宽泛,自然返回的结果集会很庞大,自然内存溢出或者响应时间很长,这样的应用你还会用吗?所以你懂的,所以内部做了一个最小输入字符长度的限制:
boolean allTermsRequired这个布尔值用于搜索阶段,意思是用户输入的关键字需要全部匹配吗?举例说明吧,我怕说的太抽象,你们看不懂。假如我们创建了索引包含了title和content两个域,那么当用户输入了搜索关键字,用户可能输入的是lucene suggest,那么程序内部首先会对用户输入的搜索关键字进行分词,得到多个Term,有了多个Term然后new多个TermQuery,那这多个TermQuery之间是or链接还是and链接呢,所以有了allTermsRequired这个参数,意思就是所有Term都需要匹配吗,说白了就是所有的TermQuery需要用and链接吗?默认很显然是false,有人可能要问了,为什么必须是要全部匹配和非全部匹配呢,如果需要实现A匹配B不匹配C又匹配D匹配E不匹配.....对不起这种条件拼接方式默认的API无法实现(当然你可以通过继承重写自己来实现),因为用户的搜索关键字分词后得到的Term的个数不确定,多个Term之间谁该包含谁不该包含,这之间的排列组合情况太多,一个boolean值表示不了这么多种情况,所以只能是要么全部and全部or,说了那么多,你们再来看源码是不是轻松多了:
private IndexW这个很明显是内部维护一个IndexWriter用来添加或更新索引数据的,protected SearcherManager searcherMgr,维护一个SearcherManager是用来获取IndexSearcher对象以及释放IndexSearcher资源的,你可以认为SearcherManager是一个IndexSearcher的工具类,
private static final Sort SORT = new Sort(new SortField("weight", SortField.Type.LONG, true));
这句是重点,创建了一个排序器,默认按照weight域进行降序排序(之所以是降序是因为最后一个reverse参数设置为true了),降序意味着weigth值越大越排前面,至于这里的weight值表示什么,取决于你的InputInterator实现,接下来就来说说InputInterator。
InputInterator接口决定了用于suggest搜索的索引数据从哪里来,说的官方点就是用于suggest搜素的索引的每个默认域的域值的数据来源需要用户来自定义,这本来也是合情合理的。
* Interface for enumerating term,weight,payload triples for s
* currently only {@link AnalyzingSuggester}, {@link
* FuzzySuggester} and {@link AnalyzingInfixSuggester} support payloads.
public interface InputIterator extends BytesRefIterator {
/** A term's weight, higher numbers mean better suggestions. */
public long weight();
/** An arbitrary byte[] to record per suggestion.
{@link LookupResult#payload} to retrieve the payload
for each suggestion. */
public BytesRef payload();
/** Returns true if the iterator has payloads */
public boolean hasPayloads();
* A term's contexts context can be used to filter suggestions.
* May return null, if suggest entries do not have any context
public Set&BytesRef& contexts();
/** Returns true if the iterator has contexts */
public boolean hasContexts();
要理解InputInterator,你首先需要理解几个概念,InputInterator里的key,content,payload,weight都表示什么含义,下面分别来说明:
key:表示用户搜索关键字域,即用户输入的搜索关键字分词后的Term在这个域上进行匹配
content:源码注释里的解释是A term's contexts context can be used to filter suggestions.太尼玛抽象了,我说的更直白更傻瓜点吧,意思就是contents是一个Term集合(只不过是用BytesRef字节形式表示的),
这个Term集合的每个元素是用来在CONTEXTS_FIELD_NAME表示的域里进行TermQuery,说白了就是在关键字的基础上再加个限制条件让返回的热词列表更符合你的要求,比如你搜iphone,可能在title域里搜索到iphone手机,可能还会返回iphone手机壳,可能你只想返回有关手机的热词不想返回有关手机壳的热词,假定你索引里还有个category类别的域,那这时你category域就是这里的context概念,你可以设置contexts的set集合为[手机],这样相当于在搜索关键字的TermQuery基础上再加一个或多个TermQuery(因为是set集合,内部会遍历set集合new多个TermQuery),记住,内部都是使用TermQuery实现查询过滤的,如果你想使用其他Query来实现过滤呢,对不起,你可以继承来重写,你懂的。
payload是用来存储一个额外信息,并以字节byte[]的形式写入索引中,当搜索返回后,你可以通过LookupResult结果对象的payload属性获取到该值,那最重要的就是要理解,为什么要设计这个payload呢,这要从LookupResult类源码中找答案: 我们在创建索引的时候通过InputInterator接口的payload方法指定了payload数据从哪来获取并将它编码为BytesRef字节的形式,然后写入索引了,然后在查询时返回的结果集是用LookupResult包装的, 如图,LookupResult包含了如下信息:
key:用户输入的搜索关键字,再返回给你
highlightKey:其实就是经过高亮的搜索关键字文本,假如你在搜索的时候设置了需要关键字高亮
value:即InputInterator接口中weight方法的返回值,即返回的当前热词的权重值,排序就是根据这个值排的
payload:就是InputInterator接口中payload方法中指定的payload信息,设计这个payload就是用来让你存一些任意你想存的信息,这就留给你们自己去发挥想象了。
contexts:同理即InputInterator接口中contexts方法的返回值再原样返回给你。
OK,还是直接上示例代码吧,或许结合示例代码再来看我说的这些,你们会更容易理解。
创建了一个产品类:
package com.yida.framework.lucene5.
import java.io.S
* @author Lanxiaowei
public class Product implements Serializable {
/** 产品名称 */
/** 产品图片 */
/** 产品销售地区 */
private String[]
/** 产品销售量 */
private int numberS
public Product(String name, String image, String[] regions, int numberSold) {
this.name =
this.image =
this.regions =
this.numberSold = numberS
public String getName() {
public void setName(String name) {
this.name =
public String getImage() {
public void setImage(String image) {
this.image =
public String[] getRegions() {
public void setRegions(String[] regions) {
this.regions =
public int getNumberSold() {
return numberS
public void setNumberSold(int numberSold) {
this.numberSold = numberS
这个类是核心,决定了你的索引是如何创建的,决定了最终返回的提示关键词列表数据及其排序。
package com.yida.framework.lucene5.
import java.io.ByteArrayOutputS
import java.io.IOE
import java.io.ObjectOutputS
import java.io.UnsupportedEncodingE
import java.util.HashS
import java.util.I
import java.util.S
import org.apache.lucene.search.suggest.InputI
import org.apache.lucene.util.BytesR
public class ProductIterator implements InputIterator {
private Iterator&Product& productI
private Product currentP
ProductIterator(Iterator&Product& productIterator) {
this.productIterator = productI
public boolean hasContexts() {
* 是否有设置payload信息
public boolean hasPayloads() {
public Comparator&BytesRef& getComparator() {
public BytesRef next() {
if (productIterator.hasNext()) {
currentProduct = productIterator.next();
//返回当前Project的name值,把product类的name属性值作为key
return new BytesRef(currentProduct.getName().getBytes("UTF8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Couldn't convert to UTF-8",e);
* 将Product对象序列化存入payload
* [这里仅仅是个示例,其实这种做法不可取,一般不会把整个对象存入payload,这样索引体积会很大,浪费硬盘空间]
public BytesRef payload() {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(currentProduct);
out.close();
return new BytesRef(bos.toByteArray());
} catch (IOException e) {
throw new RuntimeException("Well that's unfortunate.");
* 把产品的销售区域存入context,context里可以是任意的自定义数据,一般用于数据过滤
* Set集合里的每一个元素都会被创建一个TermQuery,你只是提供一个Set集合,至于new TermQuery
* Lucene底层API去做了,但你必须要了解底层干了些什么
public Set&BytesRef& contexts() {
Set&BytesRef& regions = new HashSet&BytesRef&();
for (String region : currentProduct.getRegions()) {
regions.add(new BytesRef(region.getBytes("UTF8")));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Couldn't convert to UTF-8");
* 返回权重值,这个值会影响排序
* 这里以产品的销售量作为权重值,weight值即最终返回的热词列表里每个热词的权重值
* 怎么设计返回这个权重值,发挥你们的想象力吧
public long weight() {
return currentProduct.getNumberSold();
最后就是调用suggester.lookup查询返回LookupResult结果集,Over!
package com.yida.framework.lucene5.
import java.io.IOE
import java.io.InputS
import java.util.ArrayL
import java.util.HashS
import java.util.L
import org.apache.lucene.analysis.standard.StandardA
import org.apache.lucene.search.suggest.Lookup.LookupR
import org.apache.lucene.search.suggest.analyzing.AnalyzingInfixS
import org.apache.lucene.store.RAMD
import org.apache.lucene.util.BytesR
import com.yida.framework.lucene5.util.T
* Lucene关键字提示测试
* @author Lanxiaowei
public class SuggesterTest {
private static void lookup(AnalyzingInfixSuggester suggester, String name,
String region) throws IOException {
HashSet&BytesRef& contexts = new HashSet&BytesRef&();
contexts.add(new BytesRef(region.getBytes("UTF8")));
//先以contexts为过滤条件进行过滤,再以name为关键字进行筛选,根据weight值排序返回前2条
//第3个布尔值即是否每个Term都要匹配,第4个参数表示是否需要关键字高亮
List&LookupResult& results = suggester.lookup(name, contexts, 2, true, false);
System.out.println("-- \"" + name + "\" (" + region + "):");
for (LookupResult result : results) {
System.out.println(result.key);
//从payload中反序列化出Product对象
BytesRef bytesRef = result.
InputStream is = Tools.bytes2InputStream(bytesRef.bytes);
Product product = (Product)Tools.deSerialize(is);
System.out.println("product-Name:" + product.getName());
System.out.println("product-regions:" + product.getRegions());
System.out.println("product-image:" + product.getImage());
System.out.println("product-numberSold:" + product.getNumberSold());
System.out.println();
public static void main(String[] args) {
RAMDirectory indexDir = new RAMDirectory();
StandardAnalyzer analyzer = new StandardAnalyzer();
AnalyzingInfixSuggester suggester = new AnalyzingInfixSuggester(indexDir, analyzer);
//创建Product测试数据
ArrayList&Product& products = new ArrayList&Product&();
products.add(new Product("Electric Guitar",
"http://images.example/electric-guitar.jpg", new String[] {
"US", "CA" }, 100));
products.add(new Product("Electric Train",
"http://images.example/train.jpg", new String[] { "US",
"CA" }, 100));
products.add(new Product("Acoustic Guitar",
"http://images.example/acoustic-guitar.jpg", new String[] {
"US", "ZA" }, 80));
products.add(new Product("Guarana Soda",
"http://images.example/soda.jpg",
new String[] { "ZA", "IE" }, 130));
// 创建测试索引
suggester.build(new ProductIterator(products.iterator()));
// 开始搜索
lookup(suggester, "Gu", "US");
lookup(suggester, "Gu", "ZA");
lookup(suggester, "Gui", "CA");
lookup(suggester, "Electric guit", "US");
} catch (IOException e) {
System.err.println("Error!");
OK,该说的都说了,可能说的比较啰嗦,还望见谅,希望对你们有所帮助,Demo源码还是一如既往的在底下附件里。
如果你还有什么问题请加我Q-Q:7-3-6-0-3-1-3-0-5,
或者加裙一起交流学习!
下载次数: 108
浏览: 230392 次
来自: 北京
就按你发的例子,我很丑里头的很字分词成为了,hen/h/en
“它是用来进行干预查询权重的,从而影响最终评分的,即评分公式中 ...
有几个疑问:1、LuceneUtils的closeIndexW ...
很好的数据结构提。[转] 使用 Lucene 对搜索结果排序_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
[转] 使用 Lucene 对搜索结果排序
上传于||暂无简介
阅读已结束,如果下载本文需要使用0下载券
想免费下载更多文档?
定制HR最喜欢的简历
你可能喜欢soukenan 的BLOG
用户名:soukenan
文章数:129
评论数:71
访问量:524149
注册日期:
阅读量:5863
阅读量:12276
阅读量:386120
阅读量:1077270
51CTO推荐博文
基于jar&lucene3.6.2
Field.Store.YES / NO& ---& 存储选项
设置为YES表示把这个域中的内容完全存储到文件中,方便进行文本的还原
设置为NO表示把这个域的内容不存储到文件中,但是可以被索引,此时内容无法完全还原
Field.Index.& --- 索引选项
ANALYZED& 进行分词和索引,适用于标题,内容等
NOT_ANALYZED& 进行索引,但不进行分词,适用于精确搜索
ANALYZED_NOT_NORMS 进行分词但是不存储norms信息,这个norms中包括了创建索引的时间和全职等信息
NOT_ANALYZED_NOT_NORMS 既不进行分词也不存储norms信息
NO 不进行索引
下面直接通过例子说明问题
IndexUtil.java&import&java.io.F&import&java.io.IOE&&import&org.apache.lucene.analysis.standard.StandardA&import&org.apache.lucene.document.D&import&org.apache.lucene.document.F&import&org.apache.lucene.index.CorruptIndexE&import&org.apache.lucene.index.IndexR&import&org.apache.lucene.index.IndexW&import&org.apache.lucene.index.IndexWriterC&import&org.apache.lucene.store.D&import&org.apache.lucene.store.FSD&import&org.apache.lucene.store.LockObtainFailedE&import&org.apache.lucene.util.V&&&public&class&IndexUtil&{&&&&&private&String[]&ids&=&{&1&,&2&,&3&,&4&,&5&,&6&};&&&&&private&String[]&emails&=&{&&,&&,&&,&&,&soukenan@kenan.org&,&&};&&&&&private&String[]&content&={&&&&&&&&&&&&&&Welcome&to&my&home&,&&&&&&&&&&&&&&Hello&Kenan&,&&&&&&&&&&&&&&Good&morning&,&&&&&&&&&&&&&&Are&you&OK?&,&&&&&&&&&&&&&&Yeah&hahahahahahaha&,&&&&&&&&&&&&&&I&like&foot&ball&&&&&&};&&&&&private&int[]&attachs&=&{1,4,6,2,3,8};&&&&&private&String[]&names&=&{&zhangsan&,&kenan&,&soukenan&,&Micheal&,&Liangchengpeng&,&Jah&};&&&&&&&&&&private&Directory&directory&=&null;&&&&&&&&&&private&IndexWriter&writer&=&null;&&&&&&&&&&private&Document&doc&=&null;&&&&&&&&&&private&IndexReader&reader&=&null;&&&&&public&IndexUtil(){&&&&&&&&&try&{&&&&&&&&&&&&&this.directory&=&FSDirectory.open(new&File(&d:/lucene/index02&));&&&&&&&&&}&catch&(IOException&e)&{&&&&&&&&&&&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&&&&&}&&&&&&&&&&&&public&void&buildIndex(){&&&&&&&&&try&{&&&&&&&&&&&&&writer&=&new&IndexWriter(directory,&new&IndexWriterConfig(Version.LUCENE_35,&new&StandardAnalyzer(Version.LUCENE_36)));&&&&&&&&&&&&&for(int&i=0;i&6;i++){&&&&&&&&&&&&&&&&&doc&=&new&Document();&&&&&&&&&&&&&&&&&doc.add(new&Field&(&id&,ids[i],Field.Store.YES,Field.Index.NOT_ANALYZED_NO_NORMS));&&&&&&&&&&&&&&&&&doc.add(new&Field(&email&,emails[i],Field.Store.YES,Field.Index.NOT_ANALYZED));&&&&&&&&&&&&&&&&&doc.add(new&Field(&content&,this.content[i],Field.Store.NO,Field.Index.ANALYZED));&&&&&&&&&&&&&&&&&doc.add(new&Field(&name&,this.names[i],Field.Store.YES,Field.Index.NOT_ANALYZED_NO_NORMS));&&&&&&&&&&&&&&&&&writer.addDocument(doc);&&&&&&&&&&&&&}&&&&&&&&&}&catch&(CorruptIndexException&e)&{&&&&&&&&&&&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&catch&(LockObtainFailedException&e)&{&&&&&&&&&&&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&catch&(IOException&e)&{&&&&&&&&&&&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}finally{&&&&&&&&&&&&&if(writer&!=&null&){&&&&&&&&&&&&&&&&&try&{&&&&&&&&&&&&&&&&&&&&&writer.close();&&&&&&&&&&&&&&&&&}&catch&(CorruptIndexException&e)&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&&&&&&&&&}&catch&(IOException&e)&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&}&&&&&&&&&&&&&&}&&&&&public&void&query(){&&&&&&&&&try&{&&&&&&&&&&&&&this.reader&=&IndexReader.open(directory);&&&&&&&&&&&&&System.out.println(&maxDoc:&+reader.maxDoc());&&&&&&&&&&&&&System.out.println(&numDocs:&+reader.numDocs());&&&&&&&&&&&&&&&&&&&&&&}&catch&(CorruptIndexException&e)&{&&&&&&&&&&&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&catch&(IOException&e)&{&&&&&&&&&&&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&&&&&}&}&测试类&TestCase.java&import&static&org.junit.Assert.*;&&import&org.junit.BeforeC&import&org.junit.T&&&public&class&TestCase&{&&&&&&@BeforeClass&&&&&public&static&void&setUpBeforeClass()&throws&Exception&{&&&&&}&&&&&&@Test&&&&&public&void&testBuildIndex()&{&&&&&&&&&new&IndexUtil().buildIndex();&&&&&}&&&&&@Test&&&&&public&void&testQuery(){&&&&&&&&&new&IndexUtil().query();&&&&&}&&}&
&本文出自 “” 博客,请务必保留此出处
了这篇文章
类别:┆阅读(0)┆评论(0)【lucene】 field-&term的分析过程
【lucene】 field-&term的分析过程
发布时间: 16:01:07
编辑:www.fx114.net
本篇文章主要介绍了"【lucene】 field-&term的分析过程",主要涉及到【lucene】 field-&term的分析过程方面的内容,对于【lucene】 field-&term的分析过程感兴趣的同学可以参考一下。
分析(analysis),在lucene中指的是将域(Field)转换成最基本的索引表示单元——项(Term)的过程。项的值称为语汇单元(token)。
对于英文来说,这个过程历经了提取单词、去除标点、字母转小写、去除停用词、词干还原等。
对应关系见图1-1.
图1-1 field与term的对应关系图示
2.Analyzer
org.apache.lucene.analysis.Analyzer
lucene内置有WhitespaceAnalyzer、SimpleAnalyzer、StopAnalyzer与StandardAnalyzer等分析器。
2.1 StandardAnalyzer简介
org.apache.lucene.analysis.standard.StandardAnalyzer
这是lucene最复杂的核心分析器。它包含大量的逻辑操作来识别某些种类的语汇单元(token),比如公司名称、email地址等。它的主要处理有:提取单词、去除标点、字母转小写、去除停用词。
StandardAnalyzer的createComponents()方法见下:
protected TokenStreamComponents createComponents(final String fieldName) {
final StandardTokenizer src = new StandardTokenizer();
src.setMaxTokenLength(maxTokenLength);
TokenStream tok = new StandardFilter(src);
tok = new LowerCaseFilter(tok);
tok = new StopFilter(tok, stopwords);
return new TokenStreamComponents(src, tok) {
protected void setReader(final Reader reader) {
src.setMaxTokenLength(StandardAnalyzer.this.maxTokenLength);
super.setReader(reader);
}可以清晰的看到分析过程就是reader-&tokenizer-&tokenfilter_0-&...-&tokenfilter_n-&tokens.
2.2 指定分析器
在创建IndexWriter时,会指定默认的分析器。也可以在addDocument()与updateDocument()的时候单独为某个文档指定特殊的分析器。
2.3 索引与查询两个阶段的分析器使用
QueryParser通过解析用户输入的lucene查询语法来生成Query对象,为用户带来了方便。
2.4 相关方法
org.apache.lucene.analysis.Analyzer
抽象类。分析器,用于从域(field)中构建语汇单元(tokenstream)。
TokenStream org.apache.lucene.analysis.Analyzer.tokenStream(String fieldName, String text)
它会调用createComponents()方法获得TokenStreamComponents实例,然后返回components.getTokenStream()结果。
TokenStream org.apache.lucene.analysis.Analyzer.tokenStream(String fieldName, Reader reader)
tokenStream()的重载方法。
TokenStreamComponents org.apache.lucene.analysis.Analyzer.createComponents(String fieldName)
创建这个analyzer的component。内部会set reader,set tokenizer,set 若干个tokenfilter。
2.4.1&TokenStreamComponents
org.apache.lucene.analysis.Analyzer.TokenStreamComponents
内部静态类。它封装了一个tokenstream的外部组件。它提供了对tokenizer的访问。
org.apache.lucene.analysis.Analyzer.TokenStreamComponents.TokenStreamComponents(Tokenizer source, TokenStream result)
构造函数。
TokenStream org.apache.lucene.analysis.Analyzer.TokenStreamComponents.getTokenStream()
此方法被Analyzer.tokenStream()方法调用。
2.5 自定义Analyzer
继承Analyzer这个抽象类,重写createComponents方法即可。
TokenStreamComponents org.apache.lucene.analysis.Analyzer.createComponents(String fieldName)
抽象方法,要求子类重写。
一个filter链中,不能随意调整tokenFilter的顺序。例如StopFilter对停用词是区分大小写的,那么将它置于LowerCaseFilter的前面与后面结果是不一样的。
在指定filter顺序时,还要考虑对程序性能的影响。例如我们要考虑这样一个分析器,它既能移除停用词,又能将同义词注入到语汇单元流中。此时,首先移除停用词效果会高一点,避免不必要的计算。
2.6 测试Analyzer
在索引过程中,是看不到field在背后被怎样分析的,我们可以在测试方法中直接new Analyzer对象来测试文本的分析效果。
示例工程:
3.TokenStream
org.apache.lucene.analysis.TokenStream
抽象类,具体的子类有Tokenizer与TokenFilter。
boolean org.apache.lucene.analysis.TokenStream.incrementToken()
移动游标,得到下一个token,如果tokenstream消费完了返回false。类似JDBC的ResultSet.hasNext()。
void org.apache.lucene.analysis.TokenStream.reset()
在开始调用incrementToken()方法前要先调用此方法。
3.1 TokenNizer
org.apache.lucene.analysis.Tokenizer
抽象类。TokenStream的子类,输入为reader对象。
void org.apache.lucene.analysis.Tokenizer.setReader(Reader input)
设置Tokenizer的reader。
3.2 TokenFilter
org.apache.lucene.analysis.TokenFilter
抽象类,TokenStream的子类,输入为其他的tokenstream对象,可用于迭代链式过滤。常见的子类有LowerCaseFilter,StopFilter等。
org.apache.lucene.analysis.TokenFilter.TokenFilter(TokenStream input)
构造函数,由子类通过super()方式调用,传入Tokenizer对象或tokenFilter对象。
org.apache.lucene.analysis.core.LowerCaseFilter
将token中的字母转为小写。
org.apache.lucene.analysis.core.StopFilter
移除停用词。
org.apache.lucene.analysis.en.PorterStemFilter
基于波特算法的词干过滤器。StandardAnalyzer没有用它,但我们自己可以方便地写一个Analyzer来用它。
3.3 TokenAttribute
TokenStream在得到token(语汇单元)的时候,并不会创建string来保存token,而是通过维护token在stream中的偏移位置来实现。
3.3.1&CharTermAttribute
org.apache.lucene.analysis.tokenattributes.CharTermAttribute
接口。代表token的text属性。
org.apache.lucene.analysis.tokenattributes.CharTermAttributeImpl
CharTermAttribute接口的实现类。
String org.apache.lucene.analysis.tokenattributes.CharTermAttributeImpl.toString()
输出代表token的text。
3.3.2&OffsetAttribute
org.apache.lucene.analysis.tokenattributes.OffsetAttribute
接口。描述一个token在stream中的偏移位置(起始字符与终止字符)。
本文标题:
本页链接:}

我要回帖

更多关于 lucene fieldtype 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信