首页 > 代码库 > Solr In Action 笔记(1) 之 Key Solr Concepts
Solr In Action 笔记(1) 之 Key Solr Concepts
Solr In Action 笔记(1) 之 Key Solr Concepts
题记:看了下《Solr In Action》还是收益良多的,只是奈何没有中文版,只能查看英语原版有点类,第一次看整本的英语书,就当复习下英语并顺便做下笔记吧。
1. Solr的框架
从这张图上看Solr的组件还是很齐全以及清楚明了的,但是当你看Solr源码的时候就会发现,哎呀咋看起来这么类呢。
2. Solr的查询方式
上面两张图分别举例了Solr的几个QueryComponent,比如facet,More like this,highlighting ,spatial ,以及spellcheck component 。
3. Solr的优化提示
(1) Solr支持多个Core,可以对Solr的Core按时间划分为历史Core以及现在Core,分别存放以前的历史数据以及现在的数据,或者将Core按使用类别划分。
(2) 提升Solr的并发查询性能的一个方法就是,增加shard以及replica个数,这是由于SolrCloud的查询方式是根据clusterstate.json的shard的顺序进行查询的,当shard和replica个数多的时候,对Solrcloud的并发查询就会进行分流。
4. Lucene的倒排表结构
可以通过以下表格来加深理解倒排表,通过term我们可以快速定位到具体的Document,然后再根据Document快速取出所有stored的field的内容。
5. 查询方式
5.1 BooleanQuery
BooleanQuery是很基础的一个查询方式,它就是对单个或者多个Term用AND,OR,NOT关系连接起来,它主要分为以下几种方式,假设以下的倒排表格式
通过Term Query查询new 和 home就可以分别获取他们的document如下,接下来分别对不同的boolean的方式对home和new的组合进行查询。
5.1.1 REQUIRED TERMS
+new +house
new AND house
上述两种查询虽然在逻辑上是一致的,但是在物理上还是有区别的,+是单目运算,AND是双目运算。
5.1.2 OPTIONAL TERMS
new house
new OR house
第一个查询是因为默认为操作符是OR
5.1.3 NEGATED TERMS
new house –rental
new house NOT rental
这里需要说明一下多个term的boolean查询性能,由于倒排表的特性,对多个term的boolean查询其实是需要先取出来每个term的doc然后再进行处理,也就说有几个term就需要遍历几遍,当然Lucene在这一块是有优化的,以AND为例,请看以下代码:
1 private int doNext(int doc) throws IOException { 2 for(;;) { 3 // doc may already be NO_MORE_DOCS here, but we don‘t check explicitly 4 // since all scorers should advance to NO_MORE_DOCS, match, then 5 // return that value. 6 advanceHead: for(;;) { 7 for (int i = 1; i < docsAndFreqs.length; i++) { 8 // invariant: docsAndFreqs[i].doc <= doc at this point. 9 10 // docsAndFreqs[i].doc may already be equal to doc if we "broke advanceHead"11 // on the previous iteration and the advance on the lead scorer exactly matched.12 if (docsAndFreqs[i].doc < doc) {13 docsAndFreqs[i].doc = docsAndFreqs[i].scorer.advance(doc);14 15 if (docsAndFreqs[i].doc > doc) {16 // DocsEnum beyond the current doc - break and advance lead to the new highest doc.17 doc = docsAndFreqs[i].doc;18 break advanceHead;19 }20 }21 }22 // success - all DocsEnums are on the same doc23 return doc;24 }25 // advance head for next iteration26 doc = lead.doc = lead.scorer.advance(doc);27 }28 }
首先,获取符合第一个查询条件的第一个doc ID ,记为A,
第二,遍历其他的查询条件,获取第二个查询条件的doc id,记为B,如果B大于A,说明没有即符合A又符合B的Doc ID,那么第一个查询条件就会尝试获取大于等于B的Doc ID开始新的一轮循环。
第三,如果B刚好等于A,说明即有符合A又有符合B的Doc ID,所以获取第三个查询的条件的Doc ID,记为C,再多A和C进行比较,之后就跟第二步一样。
最后,当遍历所有查询条件,如果A符合所有查询条件则说明返回A,否则就返回最大值表示没有解。
从上面的过程可以看出,多个term的查询性能还是很耗时的。
再者就是多个Term的Boolean的准确性问题:
假设我们查询New AND House,那么结果出来的Document都包含New 和 House,但是如果我查的是要求是New House 连一起的,那么用BooleanQuery出来的结果可能会包含House New,或者 new XXXXX house这样的并不符合要求的结果,这就是Boolean查询的不足之处。
最后我们来看下,Solr是怎么处理Required Term ,OPTIONAL Term, NEGATED Term的评分因子的,以上三个分别对应required,prohibited,optional
1 public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) 2 throws IOException { 3 List<Scorer> required = new ArrayList<>(); 4 List<Scorer> prohibited = new ArrayList<>(); 5 List<Scorer> optional = new ArrayList<>(); 6 Iterator<BooleanClause> cIter = clauses.iterator(); 7 for (Weight w : weights) { 8 BooleanClause c = cIter.next(); 9 Scorer subScorer = w.scorer(context, acceptDocs);10 if (subScorer == null) {11 if (c.isRequired()) {12 return null;13 }14 } else if (c.isRequired()) {15 required.add(subScorer);16 } else if (c.isProhibited()) {17 prohibited.add(subScorer);18 } else {19 optional.add(subScorer);20 }21 }22 23 if (required.size() == 0 && optional.size() == 0) {24 // no required and optional clauses.25 return null;26 } else if (optional.size() < minNrShouldMatch) {27 // either >1 req scorer, or there are 0 req scorers and at least 128 // optional scorer. Therefore if there are not enough optional scorers29 // no documents will be matched by the query30 return null;31 }32 33 // simple conjunction34 if (optional.size() == 0 && prohibited.size() == 0) {35 float coord = disableCoord ? 1.0f : coord(required.size(), maxCoord);36 return new ConjunctionScorer(this, required.toArray(new Scorer[required.size()]), coord);37 }38 39 // simple disjunction40 if (required.size() == 0 && prohibited.size() == 0 && minNrShouldMatch <= 1 && optional.size() > 1) {41 float coord[] = new float[optional.size()+1];42 for (int i = 0; i < coord.length; i++) {43 coord[i] = disableCoord ? 1.0f : coord(i, maxCoord);44 }45 return new DisjunctionSumScorer(this, optional.toArray(new Scorer[optional.size()]), coord);46 }47 48 // Return a BooleanScorer249 return new BooleanScorer2(this, disableCoord, minNrShouldMatch, required, prohibited, optional, maxCoord);50 }
5.2 短语查询
"new home" OR "new house"
"3 bedrooms" AND "walk in closet" AND "granite countertops"
在BooleanQuery中,分析了它的劣势,查询的不准性,以及性能的耗时。Phrase queries 在查找连着的term时完美的解决以上两个问题,它主要用到了Term Position。下表是带有term position的倒排表格式,term position很清楚明白的记录了,每一个term在其document的位置,虽然它增加了索引文件的大小,但是却为我们的Pharse Query带来了大大的便利。
同样出去new 和 house的信息,可以看出在document 5和8中,new 和 home是连着的,这提高了查询速度也提高了查询质量。就查询质量进行排序,PhraseQuery > BooleanQuery > FuzzyQuery
5.3 FuzzyQuery
明天继续
Solr In Action 笔记(1) 之 Key Solr Concepts