版本号:6.0.1
上两周,es从2升级到6,而我负责升级的东西,只有查询,写dsl写的快吐了,在这里吐槽下,mysql导入es的字段原汁原味的,字段都不转化的。如下图
![](https://img.haomeiwen.com/i6578571/9d3e63fdfc41da8f.png)
mmp,然后只能自己做了字段转换,言归正传,写了那么多dsl,在想底层怎么解析,一直没时间,趁着元旦写一下,底层是lucene,从lucene先看起来,es6.0.1采用lucene的版本7.0.1,下载代码导入idea,这个可以谷歌。
Query中rewrite是重新构建语法数的方法,Query子类如下图
![](https://img.haomeiwen.com/i6578571/0efdf72119749df5.png)
类没截全,太多了,其中MultiTermQuery( 抽象类)和BooleanQuery是常用的,MultiTermQuery子类如下
![](https://img.haomeiwen.com/i6578571/e5f1833c343ec678.png)
比如类似下面常遇到的查询
{
"query": {
"bool": {
"must": [
{ "term": { "title": "Search" }},
{ "wildcard": { "content": "Elasticsearch*“}}
],
"filter": [
{ "term": { "status": "published" }},
{ "range": { "publish_date": { "gte": "20190101" }}}
]
}
}
}
BooleanQuery.rewrite方法太长,不上图了,其中有一段是这样的
![](https://img.haomeiwen.com/i6578571/7f56d93fbb903cc7.png)
重写rewrite方法,所以问题回到了只看常用的MultiTermQuery类,其它的暂时不解析,类太多了。MultiTermQuery提供了三种Query树重写的方法,第一种是CONSTANT_SCORE_REWRITE,第二种SCORING_BOOLEAN_REWRITE,第三种CONSTANT_SCORE_BOOLEAN_REWRITE。
第一种使得MultiTermQuery对应的大于16个的Term看成一个Term(小于16个的term转换成BooleanQuery and形式),组成一个docid set,作为统一的倒排表参与倒排表的合并,这样无论这样的Term在索引中有多少,都只会有一个倒排表参与合并,不会产生TooManyClauses异常,也使得性能得到提高。但是多个Term之间的tf, idf等差别将被忽略.
第二种使得整个Query对象树被展开,叶子节点都为TermQuery,MultiTermQuery中的多个Term可根据在索引中的tf, idf等参与打分计算(和第三种方式的区别在于第三种方式分数是个常量),然而我们事先并不知道索引中和MultiTermQuery相对应的Term到底有多少个,因而会出现TooManyClauses异常,也即一个BooleanQuery中的子查询太多。这样会造成要合并的倒排表非常多,从而影响性能。
我们常用的是第一种方式我们只是需要模糊搜索不需要打分,所以只分析第一种,第一种打分方式是在MultiTermQueryConstantScoreWrapper.rewrite
放个demo,如下
Directory dir1 =newDirectory();
RandomIndexWriter iw1 =new RandomIndexWriter(random(), dir1);
Document doc1 =new Document();
doc1.add(newTextField("field", "foo bar", Field.Store.NO));
iw1.addDocument(doc1);
IndexReader reader1 = iw1.getReader();
iw1.close();
Directory dir2 =newDirectory();
RandomIndexWriter iw2 =new RandomIndexWriter(random(), dir2);
Document doc2 =new Document();
doc2.add(newTextField("field", "foo baz", Field.Store.NO));
iw2.addDocument(doc2);
IndexReader reader2 = iw2.getReader();
iw2.close();
BooleanQuery.Builder query =new BooleanQuery.Builder(); // Query: +foo -ba*
query.add(new TermQuery(new Term("field", "foo")), BooleanClause.Occur.MUST);
WildcardQuery wildcardQuery =new WildcardQuery(new Term("field", "ba*"));
// wildcardQuery.setRewriteMethod(MultiTermQuery.SCORING_BOOLEAN_REWRITE);
wildcardQuery.setRewriteMethod(MultiTermQuery.CONSTANT_SCORE_REWRITE);
query.add(wildcardQuery, BooleanClause.Occur.MUST_NOT);
MultiReader multireader =new MultiReader(reader1, reader2);
IndexSearcher searcher =newSearcher(multireader);
assertEquals(0, searcher.search(query.build(), 10).totalHits);
es.awaitTermination(1, TimeUnit.SECONDS);
multireader.close();
reader1.close();
reader2.close();
dir1.close();
dir2.close();
刚开始Query如图
![](https://img.haomeiwen.com/i6578571/3312db8e55bf80bb.png)
执行foo没有变化,当执行到ba*的时候截图如下
![](https://img.haomeiwen.com/i6578571/4b1d21a7a286fd84.png)
![](https://img.haomeiwen.com/i6578571/c3729852e8f4959e.png)
会把*自动补全,然后转换成一个一个的term然后去取数据,至于如何取数据和补数据,后面再说,太复杂了,这只是其中很少的一小块,还有很长的路要走。
网友评论