Lucene Manual

1. 索引

注意:在建立索引以后,内容的所有大写字母全部被转化为小写;索引更新实际是先删除原来的后创建新的

1) 存储域选项:

Field.Store.YES 或者 NO
设置为YES表示或把这个域中的内容完全存储到文件中,方便进行文本的还原
设置为NO表示把这个域的内容不存储到文件中,但是可以被索引,内容无法还原

2) 索引域选项:

Field.Index
Index.ANALYZED : 进行分词和索引,适用于标题、内容等
Index.NOT_ANALYZED : 进行索引,但是不进行分词,如果身份证,姓名,ID等,适用于精确搜索
Index.ANALYZED_NOT_NORMS : 进行分词但是不存储norms信息,这个norms中包括了创建索引的时间和权值等信息
Index.NOT_ANALYZED_NOT_NORMS : 即不进行分词也不存储norms信息
Index.NO : 不进行索引

3) 给数字和日期索引

给数字添加索引:NumericField -> new NumericField("attachs", Field.Store.YES, true).setIntValue(attachs[i])
给日期添加索引:NumericField -> new NumericField("date", Field.Store.YES, true).setLongValue(dates[i].getTime())

4) IndexReader设计

IndexReader:打开和关闭都很耗时,通常设置为单例,在类初始化时候创建,使用之后都不要关闭
IndexSearcher:通过IndexReader创建,每次使用完成后需要关闭
注意:
IndexReader是单例的,当IndexWriter修改了索引之后,已经打开的IndexReader是不会更新修改的索引,只有重新打开IndexReader才会更新
解决办法:创建一个获取reader的方法,方法中代码如下
IndexReader newReader = IndexReader.openIfChanged(oldReader);//若发生了改变,那么就返回一个新的reader,否则返回null
if(newReader != null) { reader.close();oldReader = newReader;}//关闭原来的reader,重新赋值
IndexReader也可以执行删除操作,好处是删除了索引后,reader能够马上就知道,避免使用openIfChanged方法

5) IndexWriter设计

同IndexReader一样设置为单例,每次使用完成后不关闭,调用writer.commit()提交本次操作的改变

6) Directory

FSDirectory:索引存储到硬盘中
RAMDirectory:索引存储到内存中,可以根据硬盘中的索引创建
FSDirectory.open会根据当前的运行环境打开一个最合理的基于File的Directory
RAMDirectory会从内存中打开directory,好处是速度快,缺点是无法持久化


2. 搜索

1) 精确匹配查询 (TermQuery)

IndexSearcher searcher = getSearcher();
Query query = new TermQuery(new Term(field, value));
TopDocs topDocs = searcher.search(query, num);
System.out.println("totalHits:" + topDocs.totalHits);//显示共有多少匹配项,这个和 查询num 没有关系

2) 范围查询(TermRangeQuery)

field:域
lowerTerm:开始范围
upperTerm:结束范围
includeLower:包含开区间
includeUpper:包含闭区间
new TermRangeQuery(String field, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper)
注意:这种查询方式无法查询数字类型的域

3) 数字范围查询(NumericRangeQuery)

field:域
min:开始范围
max:结束范围
minInclusive:包含开区间
maxInclusive:包含闭区间
NumericRangeQuery.newIntRange(String field, Integer min, Integer max, boolean minInclusive, boolean maxInclusive)

4) 前缀查询(PrefixQuery)

new PrefixQuery(new Term(field, value))

5) 通配符查询(WildcardQuery)
  • : 任意多个字符
    ? : 任意一个字符
    new WildcardQuery(new Term(field, value))
5) 组合查询(BooleanQuery)

BooleanQuery query = new BooleanQuery(); //可以连接多个子查询
query.add(new WildcardQuery(new Term("name", "j*")), BooleanClause.Occur.MUST);
query.add(new TermQuery(new Term("content", "like")), BooleanClause.Occur.MUST);
Occur.MUST 必须出现
Occur.SHOULD 可以出现
Occur.MUST_NOT 不能出现

6) 短语查询(PhraseQuery)

PhraseQuery query = new PhraseQuery();
query.setSlop(1); //设置中间跳过的短语数
query.add(new Term("content", "i")); //设置前一个短语
query.add(new Term("content", "football")); //设置后一个短语

7) 模糊查询(FuzzyQuery)

查询相似度最接近的

8) QueryParser:使用较多
a. 创建时设置版本,默认查询的域,分词器
QueryParser queryParser = new QueryParser(Version.LUCENE_35, "content", new StandardAnalyzer(Version.LUCENE_35));
b. 设置要查询的字符串,默认字符串被分词,每个词查询的结果被组合到了一起,可以通过一下方式设置查询结果的组合方式
queryParser.setDefaultOperator(QueryParser.Operator.AND);
Query query = queryParser.parse("like basketball");
c. 查询的时候指定查询域:
Query query = queryParser.parse("name:mike");
d. 使用通配符:
Query query = queryParser.parse("name:j*");
开启查询字符串首字母通配符匹配,默认是关闭的  效率低
queryParser.setAllowLeadingWildcard(true);
e. queryParser.parse("- name:mike + football"); //注意空格
- : 不包含
+ : 包含
f. query =queryParser.parse("id:[1 TO 3]"); 匹配一个区域,TO必须大写,开区间匹配 1,2,3
query = queryParser.parse("id:{1 TO 3}");  

匹配一个区域,TO必须大写,闭区间匹配 2
注意:TO这种方式不能匹配数字,只适用于字符串

g. AND,OR使用
query = queryParser.parse("(mike OR john) AND address:xinde"); 名字有mike或者john,地址是xinde
h. 多个词整体匹配
query = queryParser.parse("\"i like football\"");
等同于
query = queryParser.parse("i AND like AND football");
i. query = queryParser.parse(""i football"~1"); 匹配 i 和 football 之间有一个单词
j. query = queryParser.parse("name:make~"); 模糊查询
9) Lucene分页查询

不支持类似数据库的分页方式;(再查询)

  1. 每次都会查询出所有的数据,然后在取出每一页的数据,查询的速度已经很快,不用担心性能
  2. 使用searchAfter方法(推荐)
private ScoreDoc getLastScoreDoc(int page, int size, Query query, IndexSearcher searcher) throws IOException {    //获取出上一页的最后一个元素ScoreDoc
    if (page == 1) return null;
    int num = (page - 1) * size;  //从0开始  每次只取需要的数据
    TopDocs topDocs = searcher.search(query, num);
    return topDocs.scoreDocs[num - 1];
}

public void searchPageByAfter(String query, int page, int size) throws ParseException, IOException {
    IndexSearcher searcher = this.getSearcher();
    QueryParser parser = new QueryParser(Version.LUCENE_35, "content", new StandardAnalyzer(Version.LUCENE_35));
    Query q = parser.parse(query);
    ScoreDoc lastScoreDoc = getLastScoreDoc(page, size, q, searcher);//获取上一页的最后一个元素
    TopDocs topDocs = searcher.searchAfter(lastScoreDoc, q, size); //通过上一页的最后一个元素搜索下一页
    for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
        Document doc = searcher.doc(scoreDoc.doc);
        System.out.println(scoreDoc.doc + "->" + doc.get("path"));
    }
}

3. 分词

1) 3.5版本推荐的分词器:SimpleAnalyzer,StandardAnalyzer,StopAnalyzer,WhitespaceAnalyzer
2) 分词调用顺序:采用的是责任链模式,首先调用一系列的TokenFilter -> Tokenizer把输入的内容分词一个词组 ->回到TokenFilter,处理解析出来的词组

4. 排序

1) Sort

Sort.INDEXORDER :以文档编号排序 doc的id eg: searchUtils.search("java", Sort.INDEXORDER);
Sort.RELEVANCE :使用默认的评分排序 eg: searchUtils.search("java", Sort.RELEVANCE);

2) SortField 指定域排序

searchUtils.search("java", new Sort(new SortField("size",SortField.INT)); //文件大小排序
searchUtils.search("java", new Sort(new SortField("date",SortField.LONG)); //文件创建日期排序
searchUtils.search("java", new Sort(new SortField("filename",SortField.STRING)); //文件名排序
searchUtils.search("java", new Sort(new SortField("filename",SortField.STRING,true)); //文件名反转排序
searchUtils.search("java", new Sort(new SortField("size",SortField.INT),SortField.FIELD_SCORE); //先通过文件大小排序,后通过文件评分排序

5. 过滤Filter

1) TermRangeFilter 范围过滤

Filter filter = new TermRangeFilter("filename", "java.hhh", "java.kkk", true, true);
searchUtils.search("java", filter);

2) NumericRangeFilter 数字范围过滤

Filter filter =NumericRangeFilter.newIntRange("size", 500, 900, true, true);
searchUtils.search("java", filter);

3) QueryWrapperFilter 可以通过一个Query过滤

Filter filter =new QueryWrapperFilter(new WildcardQuery(new Term("filename","*.txt")));
searchUtils.search("java", filter);

6. 自定义评分

  1. 步骤:
    1.创建一个类继承于CustomScoreQuery
    2.覆盖getCustomScoreProvider
    3.创建CustomScoreProvider类
    4.覆盖customScore
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容