首页 > 解决方案 > 为什么我使用 MinHash 分析器的查询无法检索到重复项?

问题描述

我正在尝试使用其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 索引,尝试使用自定义设置和分析器,从官方示例MinHash 文档中获得灵感:

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']即使我将 my 设置为与我的语料库中的一个条目的文本完全body匹配, my也始终为空。换句话说,如果我尝试作为例如的值,我不会得到任何结果body

"I come up here for perception and clarity"

或子字符串,如

"I come up here for perception"

虽然理想情况下,我希望该过程返回近似重复项,分数是通过 MinHash 获得的查询和近似重复项的 Jaccard 相似性的近似值。

我的查询和/或索引 Elasticsearch 的方式有问题吗?我是否完全错过了其他东西?

PS:您可以查看https://github.com/davidefiocco/dockerized-elasticsearch-duplicate-finder/tree/ea0974363b945bf5f85d52a781463fba76f4f987以获取非功能性但希望可重现的示例(我也会在找到一个解决方案!)

标签: pythonelasticsearchduplicateselasticsearch-pyminhash

解决方案


以下是您应该仔细检查的一些事情,因为它们可能是罪魁祸首:

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

      "mappings": {
          "properties": {
              "text": {"type": "text", "analyzer": "my_analyzer"}
          }
    
  • 在索引阶段,您还可以按照文档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']]
    

推荐阅读