Fra*_*erZ 18 java lucene elasticsearch
我目前正在编写一个程序,目前使用elasticsearch作为后端数据库/搜索索引.我想模仿当前使用匹配查询的/_search端点的功能:
{
"query": {
"match" : {
"message" : "Neural Disruptor"
}
}
}
Run Code Online (Sandbox Code Playgroud)
做一些示例查询,在庞大的魔兽世界数据库中产生以下结果:
Search Term Search Result
------------------ -----------------------
Neural Disruptor Neural Needler
Lovly bracelet Ruby Bracelet
Lovely bracelet Lovely Charm Bracelet
Run Code Online (Sandbox Code Playgroud)
在查看elasticsearch的文档后,我发现匹配查询相当复杂.在java中使用lucene模拟匹配查询的最简单方法是什么?(它似乎做了一些模糊匹配,以及寻找术语)
导入MatchQuery的弹性搜索代码(我相信org.elasticsearch.index.search.MatchQuery)似乎并不那么容易.它被大量嵌入到Elasticsearch中,并且看起来不像是可以轻松拔出的东西.
我不需要一个完整的证据"必须完全匹配elasticsearch匹配",我只需要一些接近,或者可以模糊匹配/找到最佳匹配.
发送到端点q=参数的任何_search内容都由query_string查询(非 org.elasticsearch.index.search.MatchQuery)理解Lucene表达式语法使用.
使用JavaCC在Lucene项目中定义查询解析器语法,如果您想查看,可以在此处找到语法.最终产品是一个叫做的类QueryParser(见下文).
负责解析查询字符串的ES源代码中QueryStringQueryParser的QueryParser类是委托给Lucene的类(由JavaCC生成).
所以基本上,如果你得到一个等价的查询字符串_search?q=...,那么你可以使用该查询字符串,QueryParser.parse("query-string-goes-here")并Query使用Lucene 运行reified .
自从我直接与lucene合作以来已经有一段时间了,但最初你想要的应该是相当简单的.lucene查询的基本行为与匹配查询非常相似(query_string完全等同于lucene,但匹配非常接近).如果你想尝试一下,我把一个与lucene(7.2.1)一起工作的小例子放在一起.主要代码如下:
public static void main(String[] args) throws Exception {
// Create the in memory lucence index
RAMDirectory ramDir = new RAMDirectory();
// Create the analyzer (has default stop words)
Analyzer analyzer = new StandardAnalyzer();
// Create a set of documents to work with
createDocs(ramDir, analyzer);
// Query the set of documents
queryDocs(ramDir, analyzer);
}
private static void createDocs(RAMDirectory ramDir, Analyzer analyzer)
throws IOException {
// Setup the configuration for the index
IndexWriterConfig config = new IndexWriterConfig(analyzer);
config.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
// IndexWriter creates and maintains the index
IndexWriter writer = new IndexWriter(ramDir, config);
// Create the documents
indexDoc(writer, "document-1", "hello planet mercury");
indexDoc(writer, "document-2", "hi PLANET venus");
indexDoc(writer, "document-3", "howdy Planet Earth");
indexDoc(writer, "document-4", "hey planet MARS");
indexDoc(writer, "document-5", "ayee Planet jupiter");
// Close down the writer
writer.close();
}
private static void indexDoc(IndexWriter writer, String name, String content)
throws IOException {
Document document = new Document();
document.add(new TextField("name", name, Field.Store.YES));
document.add(new TextField("body", content, Field.Store.YES));
writer.addDocument(document);
}
private static void queryDocs(RAMDirectory ramDir, Analyzer analyzer)
throws IOException, ParseException {
// IndexReader maintains access to the index
IndexReader reader = DirectoryReader.open(ramDir);
// IndexSearcher handles searching of an IndexReader
IndexSearcher searcher = new IndexSearcher(reader);
// Setup a query
QueryParser parser = new QueryParser("body", analyzer);
Query query = parser.parse("hey earth");
// Search the index
TopDocs foundDocs = searcher.search(query, 10);
System.out.println("Total Hits: " + foundDocs.totalHits);
for (ScoreDoc scoreDoc : foundDocs.scoreDocs) {
// Get the doc from the index by id
Document document = searcher.doc(scoreDoc.doc);
System.out.println("Name: " + document.get("name")
+ " - Body: " + document.get("body")
+ " - Score: " + scoreDoc.score);
}
// Close down the reader
reader.close();
}
Run Code Online (Sandbox Code Playgroud)
扩展它的重要部分将是分析器和理解lucene 查询解析器语法.
该Analyzer所使用的索引和查询,告诉双方如何解析文本,以便他们可以考虑以同样的方式文本.它设置了如何标记(分割什么,是否toLower()等).在StandardAnalyzer对空间和其他一些分裂(我没有这个方便)和看起来也适用TOLOWER().
这QueryParser将为你做一些工作.如果你在我的例子中看到上文.我做两件事,我告诉解析器默认字段是什么,我传递一串字符串hey earth.解析器将把它变成一个看起来像的查询body:hey body:earth.这将查找具有hey或earth在其中的文档body.将找到两份文件.
如果我们要传递hey AND earth的查询被解析为看起来像+body:hey +body:earth需要docs同时拥有这两个术语.将找到零文件.
要应用模糊选项,请在要模糊~的术语中添加一个.因此,如果查询是hey~ earth它将应用模糊,hey查询将是如下body:hey~2 body:earth.将找到三份文件.
您可以更直接地编写查询,解析器仍然处理事情.因此,如果你传递它hey name:\"document-1\"(它的标记分裂-),它将创建一个类似的查询body:hey name:"document 1".在查找短语时将返回两个文档document 1(因为它仍然在其上标记-).如果我这样hey name:document-1写body:hey (name:document name:1)它会返回所有文件,因为它们都有document作为一个术语.在这里理解有一些细微差别.
我将尝试更多地介绍它们的相似之处.引用匹配查询.Elastic说主要区别是,"它不支持字段名称前缀,通配符或其他"高级"功能." 这些可能会更加突出另一个方向.
当使用分析字段时,匹配查询和lucene查询都将获取查询字符串并将分析器应用于它(将其标记化为toLower等).因此,他们将变成HEY Earth查找条款hey或查询的查询earth.
匹配查询可以operator通过提供来设置"operator" : "and".这改变了我们的查询以寻找hey和earth.lucene中的类比是做类似的事情parser.setDefaultOperator(QueryParser.Operator.AND);
接下来是模糊.两者都使用相同的设置.我相信弹性"fuzziness": "AUTO"在应用于~查询时相当于lucene的自动(虽然我认为你必须自己每个术语添加它,这有点麻烦).
零项查询似乎是一个弹性结构.如果您想要ALL设置,则在查询解析器从查询中删除所有标记时,您必须复制匹配所有查询.
截止frequery看起来是有关CommonTermsQuery.我没有用过这个,所以如果你想使用它,你可能会有一些挖掘.
Lucene有一个同义词过滤器可以应用于分析器,但您可能需要自己构建地图.
您可能会发现的差异可能在于评分.当我运行时,他们查询hey earthlucene.它得到的文档-3和文档-4都返回得分1.3862944.当我以下列形式运行查询时:
curl -XPOST http://localhost:9200/index/_search?pretty -d '{
"query" : {
"match" : {
"body" : "hey earth"
}
}
}'
Run Code Online (Sandbox Code Playgroud)
我得到相同的文件,但得分1.219939.您可以对它们进行解释.在lucene中打印每个文档
System.out.println(searcher.explain(query, scoreDoc.doc));
Run Code Online (Sandbox Code Playgroud)
并且通过查询每个文档来弹性
curl -XPOST http://localhost:9200/index/docs/3/_explain?pretty -d '{
"query" : {
"match" : {
"body" : "hey earth"
}
}
}'
Run Code Online (Sandbox Code Playgroud)
我得到了一些分歧,但我不能完全解释它们.我确实得到了doc的值,1.3862944但是fieldLength它不同并影响了重量.
| 归档时间: |
|
| 查看次数: |
608 次 |
| 最近记录: |