随机排序和分页 Elasticsearch

41
这个问题中,提出了使用可选种子排序的功能请求,以便重新创建随机顺序。
我需要能够分页随机排序的结果。在 Elasticsearch 0.19.1中如何实现?
谢谢。
6个回答

76

1
谢谢你的帮助。我认为如果你更新示例查询,答案会更清晰地表明random_score支持种子。 - Jonas Anso
1
@MbRostami 对于过滤器,您必须添加 "boost_mode": "replace",,请参见 https://dev59.com/D1sX5IYBdhLWcg3wLdCJ#48338880。 - bato3

47
您可以使用唯一字段(例如id)和随机盐的哈希函数进行排序。根据结果需要多么随机,您可以做像这样原始的事情:
{
  "query" : { "query_string" : {"query" : "*:*"} },
  "sort" : {
    "_script" : { 
        "script" : "(doc['_id'].value + salt).hashCode()",
        "type" : "number",
        "params" : {
            "salt" : "some_random_string"
        },
        "order" : "asc"
    }
  }
}

或者像一些复杂的东西那样

{
  "query" : { "query_string" : {"query" : "*:*"} },
  "sort" : {
    "_script" : { 
        "script" : "org.elasticsearch.common.Digest.md5Hex(doc['_id'].value + salt)",
        "type" : "string",
        "params" : {
            "salt" : "some_random_string"
        },
        "order" : "asc"
    }
  }
}
第二个示例将产生更多的随机结果,但速度会稍慢。
为使该方法可行,必须存储字段_id。否则,查询将会失败并引发NullPointerException异常。

我应该将字符串存储在客户端吗?例如,在cookie中?这样当用户调用第二页时,相同的顺序就会被保留下来? - Yeggeps
1
盐字符串应该在维护用户会话的层上生成和存储。它可以与存储用户查询或当前显示的页面编号的位置相同。它也可以是cookie。 - imotov
提醒一下,在实现这个解决方案时,使用索引超过 10M 的文档会大大增加数据节点的 CPU 使用率。我预计会有所增加,但没想到会把服务器的性能跑满。 - Michael Love

25

imotov提供了一个好的解决方案。

这里有一个更简单的解决方案,你不需要依赖于文档属性:

{
  "query" : { "query_string" : {"query" : "*:*"} },
  "sort" : {
    "_script" : { 
        "script" : "Math.random()",
        "type" : "number",
        "params" : {},
        "order" : "asc"
    }
  }
}

如果你想设置一个范围,那么可以这样做:

{
  "query" : { "query_string" : {"query" : "*:*"} },
  "sort" : {
    "_script" : { 
        "script" : "Math.random() * (myMax - myMin) + myMin",
        "type" : "number",
        "params" : {},
        "order" : "asc"
    }
  }
}

将最大值和最小值替换为适当的值。


6
这是一个很好的通用解决方案。然而,原问题要求“可选的种子,允许重新创建随机顺序”,这就是所有复杂性的来源。 - imotov
是的,你说得完全正确。我的解决方案更适合标题“Elasticsearch随机排序和分页”。对Yeggeps的需求完全不足够。 - DavidGOrtega
很好的答案,但不幸的是这并不能消除脚本排序的额外开销……仍然会在查询200万个文档时添加超过1秒的时间。 - Nariman
我在每100个结果(大小为20)中获得大约10个重复项。如何消除重复项? - Vingtoft

4

我最终解决了这个问题,但方法略有不同于imotov的建议。因为我有多个客户端,我不想在每个客户端上都实现盐字符串周围的逻辑。

我已经在模型上有一个随机化密钥。我也不需要每次请求时都是随机顺序,所以我创建了一个定时作业,在晚上更新随机密钥,然后按该字段在Elasticsearch中进行排序。


3

新格式:

{
    "sort": {
        "_script": {
            "type": "number",
            "script": {
                "source": "Math.random()",
                "lang": "painless"
            },
            "order": "asc"
        }
    }
}

0

嗯,我一直在考虑这个问题,上面的所有方法似乎都有点“太复杂”了,对于应该相对简单的事情来说。所以我想出了一个替代方案,完全可以正常工作,而不需要“发疯”。

我首先执行一个_count查询,然后将其与“Start”和rand(0,$count)组合起来。

例如:

JSONArray = array of json to send to ElasticSearch
$total_results = $ElasticSearchClient->count(JSONArray)
$start = rand(0, $total_results)
JSONArray['body']['from'] = $start;
$ElasticSearchClient->search(JSONArray);

以上示例的假设:

  • 您正在运行PHP
  • 您还在使用PHP客户端

但是您不需要使用PHP来实现这个方法,该方法适用于任何示例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接