为什么我使用 MinHash 分析器的查询无法检索重复项?

Why does my query using a MinHash analyzer fail to retrieve duplicates?

我正在尝试使用其 MinHash implementation 查询 Elasticsearch 索引中的近似重复项。 我在容器中使用 Python 客户端 运行 来索引和执行搜索。

我的语料库是一个有点像这样的 JSONL 文件:

{"id":1, "text":"I'd just like to interject for a moment"}
{"id":2, "text":"I come up here for perception and clarity"}
...

我成功创建了一个 Elasticsearch 索引,尝试使用自定义设置和分析器,灵感来自 official examples and MinHash docs:

def create_index(client):
    client.indices.create(
        index="documents",
        body={
            "settings": {
                "analysis": {
                    "filter": {
                        "my_shingle_filter": {      
                        "type": "shingle",
                        "min_shingle_size": 5,
                        "max_shingle_size": 5,
                        "output_unigrams": False
                        },
                        "my_minhash_filter": {
                        "type": "min_hash",
                        "hash_count": 10,          
                        "bucket_count": 512,      
                        "hash_set_size": 1,       
                        "with_rotation": True     
                        }
                    },
                    "analyzer": {
                        "my_analyzer": {
                        "tokenizer": "standard",
                        "filter": [
                            "my_shingle_filter",
                            "my_minhash_filter"
                        ]
                        }
                    }
                }
            },
            "mappings": {
                "properties": {
                    "name": {"type": "text", "analyzer": "my_analyzer"}
                }
            },
        },
        ignore=400,
    )

我通过 Kibana 验证索引创建没有大问题,还通过访问 http://localhost:9200/documents/_settings 我得到了一些看起来有序的东西:

然而,查询索引:

def get_duplicate_documents(body, K, es):
    doc = {
        '_source': ['_id', 'body'],
        'size': K,
        'query': {
            "match": {
                "body": {
                    "query": body,
                    "analyzer" : "my_analyzer"
                }
            }
        }
    }

    res = es.search(index='documents', body=doc)
    top_matches = [hit['_source']['_id'] for hit in res['hits']['hits']]

我的 res['hits'] 始终是空的,即使我将我的 body 设置为 完全匹配 我的语料库中的一个条目的文本。换句话说,如果我尝试作为 body 的值,我不会得到任何结果,例如

"I come up here for perception and clarity"

或像

这样的子字符串
"I come up here for perception"

虽然理想情况下,我希望程序 return 接近重复,分数是通过 MinHash 获得的查询和接近重复的 Jaccard 相似度的近似值。

我的查询 and/or 索引 Elasticsearch 的方式有问题吗?我是否完全遗漏了其他东西?

P.S.: 您可以查看 https://github.com/davidefiocco/dockerized-elasticsearch-duplicate-finder/tree/ea0974363b945bf5f85d52a781463fba76f4f987 的非功能性但希望可重现的示例(我也会在找到解决方案时更新回购协议!)

这里有一些你应该 double-check 的东西,因为它们很可能是罪魁祸首:

  • 创建映射时,您应该在 body 参数内的 client.indices.create 方法中将“名称”更改为“文本”,因为您的 json 文档有一个名为 text:

    的字段
      "mappings": {
          "properties": {
              "text": {"type": "text", "analyzer": "my_analyzer"}
          }
    
  • 在索引阶段,您还可以在 the documentation 之后修改 generate_actions() 方法,例如:

    for elem in corpus:
      yield {
          "_op_type": "index"
          "_index": "documents",
          "_id": elem["id"],
          "_source": elem["text"]
      }
    

    顺便说一下,如果你正在索引 pandas 数据帧,你可能需要检查实验官方库 eland

  • 此外,根据您的映射,您正在使用 minhash 标记过滤器,因此 Lucene 将在散列中的 text 字段内转换您的文本。因此,您可以使用散列而不是字符串查询该字段,就像您在示例 "I come up here for perception and clarity" 中所做的那样。 所以最好的使用方式是检索字段text的内容,然后在Elasticsearch中查询检索到的相同值。然后 _id 元字段不在 _source 元字段内,因此您应该更改 get_duplicate_documents() 方法:

    def get_duplicate_documents(body, K, es):
      doc = {
          '_source': ['text'],
          'size': K,
          'query': {
              "match": { 
                  "text": { # I changed this line!
                      "query": body
                  }
              }
          }
      }
    
      res = es.search(index='documents', body=doc)
      # also changed the list comprehension!
      top_matches = [(hit['_id'], hit['_source']) for hit in res['hits']['hits']]