模仿Elasticsearch MatchQuery

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匹配",我只需要一些接近,或者可以模糊匹配/找到最佳匹配.

Val*_*Val 8

发送到端点q=参数的任何_search内容都由query_string查询(非 org.elasticsearch.index.search.MatchQuery)理解Lucene表达式语法使用.

使用JavaCC在Lucene项目中定义查询解析器语法,如果您想查看,可以在此处找到语法.最终产品是一个叫做的类QueryParser(见下文).

负责解析查询字符串的ES源代码中QueryStringQueryParserQueryParser类是委托给Lucene的类(由JavaCC生成).

所以基本上,如果你得到一个等价的查询字符串_search?q=...,那么你可以使用该查询字符串,QueryParser.parse("query-string-goes-here")Query使用Lucene 运行reified .


pho*_*dka 6

自从我直接与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.这将查找具有heyearth在其中的文档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-1body:hey (name:document name:1)它会返回所有文件,因为它们都有document作为一个术语.在这里理解有一些细微差别.


我将尝试更多地介绍它们的相似之处.引用匹配查询.Elastic说主要区别是,"它不支持字段名称前缀,通配符或其他"高级"功能." 这些可能会更加突出另一个方向.

当使用分析字段时,匹配查询和lucene查询都将获取查询字符串并将分析器应用于它(将其标记化为toLower等).因此,他们将变成HEY Earth查找条款hey或查询的查询earth.

匹配查询可以operator通过提供来设置"operator" : "and".这改变了我们的查询以寻找heyearth.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它不同并影响了重量.