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

时间:2014-07-24 23:13:37

标签: 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]}
'

我上传的其他示例文档:

{"tags": [1,4,6,7]}
{"tags": [2,3,5,6]}
{"tags": [1,2,3,8]}

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

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

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

{
  "my_project" : {
    "mappings" : {
      "img" : {
        "properties" : {
          "tags" : {
            "type" : "string"
          }
        }
      }
    }
  }
}

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

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


到目前为止我的解决方案

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

curl -XPUT 'http://localhost:9200/my_project/img/1' -d '
 {"tags": "1 4 6 7 9"}
' 

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

2 个答案:

答案 0 :(得分:7)

您可以使用function score query公式Euclidian distance来获得所需内容。

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

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
     }
 }'

停止Elasticsearch并在$ES_HOME/config/scripts中创建名为“euclidian_distance.mv​​el”的脚本文件并添加此脚本。

_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)

重新启动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
             }
         }
      }
   }
}

将首先返回值为1,2,3,8的标记对象。

答案 1 :(得分: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