在elasticsearch中查找大多数相似的整数数组

Ond*_*dra 9 elasticsearch

改写:

在我的项目中,我有图像.每个图像都有5个范围[1,10]的标签.我使用Elasticsearch上传这些标签:

我将这些文档加载到索引"my_project"中的elasticsearch中,类型为"img":

curl -XPUT 'http://localhost:9200/my_project/img/1' -d '
 {"tags": [1,4,6,7,9]}
'
Run Code Online (Sandbox Code Playgroud)

我上传的其他示例文档:

{"tags": [1,4,6,7]}
{"tags": [2,3,5,6]}
{"tags": [1,2,3,8]}
Run Code Online (Sandbox Code Playgroud)

在我的应用程序中,向量更长,但具有固定数量的唯一元素.而且我喜欢这些文件的20M.

现在我想找到给定向量的类似文档.当矢量具有更常见的标签时,矢量更相似.因此,例如,我想找到整数向量的大多数类似文档[1,2,3,7].最佳匹配应该是最后一个示例文档{"tags": [1,2,3,8]},因为它们在其标记中共享3个公共值,[1,2,3]比任何其他向量更常见的值.

所以这是我的问题.如果我使用上面的CURL命令上传文档,我会得到以下映射:

{
  "my_project" : {
    "mappings" : {
      "img" : {
        "properties" : {
          "tags" : {
            "type" : "string"
          }
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

但我认为正确的映射应该使用整数而不是字符串.如何为此类数据进行正确的显式映射?

现在我想用上面的相似度算法搜索文档.如何使用上面解释的相似度算法获得上述类型的100个最相似的文档?如果我将这些向量转换为具有空格分隔数字的字符串,我将能够使用带有for语句的布尔查询进行此搜索,但我认为使用整数数组应该更快.你能告诉我,我怎样才能为elasticsearch构建搜索查询?


到目前为止我的解

我现在使用的基本解决方案是将整数数组转换为字符串.所以我将文件保存为:

curl -XPUT 'http://localhost:9200/my_project/img/1' -d '
 {"tags": "1 4 6 7 9"}
' 
Run Code Online (Sandbox Code Playgroud)

然后基本上搜索字符串"1 2 3".虽然这在某种程度上起作用,但我认为将整数数组保存为整数数组而不是字符串会更正确和更快.是否可以像使用整数数组一样使用elasticsearch中的整数数组?也许我的字符串方法是最好的,不能/不必在elasticsearch中显式使用整数数组.

Dan*_*ery 8

您可以使用使用欧几里德距离公式的功能评分查询获得您想要的内容.

删除当前映射并索引文档:

curl -XPUT 'http://localhost:9200/my_project/img/1' -d '
{
    "tags": {
        "tag1": 1,
        "tag2": 4,
        "tag3": 6,
        "tag4": 7
    }
}'

curl -XPUT 'http://localhost:9200/my_project/img/2' -d '
{
    "tags": {
        "tag1": 2,
        "tag2": 3,
        "tag3": 5,
        "tag4": 6
     }
 }'

 curl -XPUT 'http://localhost:9200/my_project/img/3' -d '
 {
     "tags": {
        "tag1": 1,
        "tag2": 2,
        "tag3": 3,
        "tag4": 8
     }
 }'
Run Code Online (Sandbox Code Playgroud)

停止Elasticsearch并创建一个名为'euclidian_distance.mv​​el'的脚本文件$ES_HOME/config/scripts并添加此脚本.

_score * pow(sqrt(pow(doc['tags.tag1'].value - param1, 2)) + sqrt(pow(doc['tags.tag2'].value - param2, 2)) + sqrt(pow(doc['tags.tag3'].value - param3, 2)) + sqrt(pow(doc['tags.tag4'].value - param4, 2)), -1)
Run Code Online (Sandbox Code Playgroud)

重新启动Elastisearch并运行此查询:

curl -XPOST 'http://localhost:9200/my_project/' -d '
{
    "query": {
        "function_score": {
            "query": {
                "match_all": {}
         },
         "script_score": {
             "script": "euclidian_distance",
             "params": {
                 "param1": 1,
                 "param2": 2,
                 "param3": 3,
                 "param4": 7
             }
         }
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

1,2,3,8将首先返回带有值的标记对象.


Joh*_*one 2

我会在去年的 Elasticsearch 邮件列表上查看去年的讨论。另一个 ES 用户正在尝试做你想做的事情,匹配数组元素并按相似度排序。在他的例子中,他的数组成员是“一”、“二”、“三”等,但它们几乎相同:

http://elasticsearch-users.115913.n3.nabble.com/Similarity-score-in-array-td4041674.html

讨论中指出的问题是,没有什么可以让你真正得到你想要的开箱即用的东西。您使用数组成员的方法(字符串或整数,我认为两者都可以)将使您接近,但可能与您想要实现的目标有一些差异。原因是Elasticsearch(以及Lucene/Solr)中默认的相似度评分机制是TF/IDF:http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/relevance-intro.html

TF/IDF 可能非常接近,并且根据用例可能会给出相同的结果,但不能保证这样做。出现频率很高的标签(假设“1”的频率是“2”的两倍)将更改每个术语的权重,这样您可能无法准确获得所需的内容。

如果您需要精确的评分/相似性算法,我相信您将需要对其进行自定义评分。正如您所发现的,自定义评分脚本无法很好地扩展,因为该脚本将为每个文档运行,因此它一开始并不会太快,并且响应时间会以线性方式衰减。

就我个人而言,我可能会尝试 Elasticsearch 提供的一些相似性模块,例如 BM25:

http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/index-modules-similarity.html