Elasticsearch Terms聚合结果如何按key字符串长度排序
解决方案
方案1:按字符串长度升序排序
利用terms聚合支持按子聚合结果排序的特性,新增一个指标聚合计算每个桶key的字符串长度,再将terms的排序规则指定为按该指标升序即可。查询语句如下:
{ "query": {其他查询约束}, "aggs": { "my_values": { "terms": { "field": "foo.raw", "include": ".*bar.*", "order": {"key_length": "asc"}, "size": N }, "aggs": { "key_length": { "min": { "script": { "source": "doc['foo.raw'].value.length()", "lang": "painless" } } } } } } }
说明:同一个桶内所有文档的foo.raw值相同,计算出来的长度唯一,因此这里min聚合可以替换为max/avg,效果完全一致。如果你的ES集群禁用了inline脚本,也可以在写入数据时提前把字符串长度预处理成独立的数值字段,查询时直接用该字段做子聚合排序,性能更好。
方案2:按与搜索子串的相似度排序
如果需要更精准的匹配度排序,可以用编辑距离(Levenshtein距离)作为排序依据,和子串差异越小的key排序越靠前:
{ "query": {其他查询约束}, "aggs": { "my_values": { "terms": { "field": "foo.raw", "include": ".*bar.*", "order": {"similarity_score": "asc"}, "size": N }, "aggs": { "similarity_score": { "min": { "script": { "source": """ String key = doc['foo.raw'].value; String target = "bar"; int[][] dp = new int[key.length() + 1][target.length() + 1]; for (int i = 0; i <= key.length(); i++) dp[i][0] = i; for (int j = 0; j <= target.length(); j++) dp[0][j] = j; for (int i = 1; i <= key.length(); i++) { for (int j = 1; j <= target.length(); j++) { int cost = key.charAt(i-1) == target.charAt(j-1) ? 0 : 1; dp[i][j] = Math.min(Math.min(dp[i-1][j] + 1, dp[i][j-1] + 1), dp[i-1][j-1] + cost); } } return dp[key.length()][target.length()]; """, "lang": "painless" } } } } } } }
说明:如果追求更高性能,也可以根据业务需求简化相似度计算规则,比如统计子串在key中的出现位置、前后缀匹配权重等,自定义排序逻辑。
内容的提问来源于stack exchange,提问作者AbrahamCoding
相关产品推荐
相关产品推荐

