自从我直接使用 lucene 以来已经有一段时间了,但你想要的应该是,最初,相当简单。 lucene 查询的基本行为与 match 查询非常相似(query_string 完全等同于 lucene,但 match 非常接近)。我整理了一个small example,如果你想尝试一下,它只适用于 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();
}
扩展它的重要部分将是 analyzer 和理解 lucene query parser syntax。
索引和查询都使用Analyzer 来告诉两者如何解析文本,以便他们可以以相同的方式思考文本。它设置了如何标记化(拆分什么,是否 toLower() 等)。 StandardAnalyzer 拆分空间和其他几个(我没有这个方便),并且看起来也适用于 toLower()。
QueryParser 将为您完成一些工作。如果您在我的示例中看到上面的内容。我做了两件事,我告诉解析器默认字段是什么,然后我传递一个hey earth 的字符串。解析器将把它变成一个看起来像body:hey body:earth 的查询。这将查找在body 中具有hey 或earth 的文档。将找到两个文档。
如果我们要传递hey AND earth,则查询将被解析为类似于+body:hey +body:earth,这将要求文档同时具有这两个术语。将找到零个文档。
要应用模糊选项,您可以将~ 添加到要模糊的术语中。因此,如果查询是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作为术语。理解这里有一些细微差别。
我将尝试更多地介绍它们的相似之处。引用match query。 Elastic 表示,主要区别在于,“它不支持字段名称前缀、通配符或其他“高级”功能。”这些可能会在另一个方向上更加突出。
match 查询和 lucene 查询在处理分析字段时都会获取查询字符串并将分析器应用于它(对其进行标记、toLower 等)。因此,它们都会将HEY Earth 转换为查找术语hey 或earth 的查询。
匹配查询可以通过提供"operator" : "and" 来设置operator。这将我们的查询更改为查找hey 和earth。 lucene 中的类比是做类似parser.setDefaultOperator(QueryParser.Operator.AND);
接下来是fuzziness。两者都使用相同的设置。我相信在将~ 应用于查询时,弹性的"fuzziness": "AUTO" 相当于lucene 的自动(尽管我认为您必须自己添加每个术语,这有点麻烦)。
零词查询似乎是一种弹性结构。如果您想要 ALL 设置,如果查询解析器从查询中删除了所有标记,则必须复制 match all 查询。
截止频率查询似乎与CommonTermsQuery 有关。这个我没用过,如果你想用的话,你可能需要一些挖掘。
Lucene 有一个 synonym filter 可应用于分析器,但您可能需要自己 build the map。
您可能会发现不同之处可能在于得分。当我运行时,他们针对 lucene 查询 hey earth。它得到的 document-3 和 document-4 都返回了1.3862944 的分数。当我以以下形式运行查询时:
curl -XPOST http://localhost:9200/index/_search?pretty -d '{
"query" : {
"match" : {
"body" : "hey earth"
}
}
}'
我得到了相同的文件,但得分为1.219939。您可以对它们进行解释。在 lucene 中通过使用
打印每个文档
System.out.println(searcher.explain(query, scoreDoc.doc));
并且在弹性中通过像这样查询每个文档
curl -XPOST http://localhost:9200/index/docs/3/_explain?pretty -d '{
"query" : {
"match" : {
"body" : "hey earth"
}
}
}'
我得到了一些差异,但我无法准确地解释它们。我确实得到了1.3862944 文档的值,但fieldLength 不同,这会影响重量。