1

我有一个包含不同目录产品的搜索索引。现在,当我搜索给定的搜索词时,通常会返回如下结果:

Catalog 1 - Product 1
Catalog 1 - Product 2
Catalog 1 - Product 3
...
Catalog 1 - Product x
Catalog 2 - Product 1
...

这不是最佳的,因为我想将用户指向其他目录,同时,也不需要他浏览包含同一目录的所有产品的多个搜索结果页面。所以我尝试使用多元化的采样器聚合,它与一个孩子一起使用top_hits-aggregation似乎正是我想要的解决方案:

POST /myIndex/_search?typed_keys=true
{
  "query": {
    "query_string": {
      "fields": [
        "title^2",
        "description^2",
        "descriptionOriginal^0.01"
      ],
      "query": "*someSearchTerm*"
    }
  },
  "size": 0,
  "aggs": {
    "aggDiversifiedSampler": {
      "diversified_sampler": {
        "shard_size": 100000,
        "field": "catalogId",
        "max_docs_per_value": 3
      },
      "aggs": {
        "aggTopHits": {
          "top_hits": {
            "from": 0,
            "size": 50,
            "sort": [
              {
                "_score": {
                  "order": "desc"
                }
              }
            ]
          }
        }
      }
    }
  }
}

分页是通过内部 top_hits-aggregation 的“size”和“from”属性完成的。可以从内部 top_hits-aggregation 的值集合中获取搜索结果 - 因此我将查询本身的大小设置为 0。

这似乎有效 - 乍一看,但仔细查看结果会发现,并非所有搜索结果都被返回。结果现在看起来像这样:

Catalog 1 - Product 1
Catalog 1 - Product 2
Catalog 1 - Product 3
Catalog 2 - Product 1
Catalog 2 - Product 2
Catalog 2 - Product 3
...
Catalog x - Product 1
Catalog x - Product 2
Catalog x - Product 3

……然后就结束了。

看起来,diversively_sampler 在到达最后一个目录后不会扭曲,因此不会出现来自单个目录的进一步结果。我想要的是这样的:

Catalog 1 - Product 1
Catalog 1 - Product 2
Catalog 1 - Product 3
Catalog 2 - Product 1
Catalog 2 - Product 2
Catalog 2 - Product 3
...
Catalog x - Product 1
Catalog x - Product 2
Catalog x - Product 3
Catalog 1 - Product 4
Catalog 1 - Product 5
Catalog 1 - Product 6
Catalog 2 - Product 4
Catalog 2 - Product 5
Catalog 2 - Product 6
...

有任何想法吗?我使用多元化采样器的技术并没有一成不变,但我想不出别的办法。也许是一些花哨的基于脚本的查询排序?不知道。基于客户端的重新排序不是一种选择,因为我不希望弹性搜索方式的分页被破坏。我需要分页来保持性能 - 搜索索引约为 18GB,包含 900k 文档......

4

1 回答 1

1

我想我找到了一个没有使用脚本排序的 compatible_sampler-aggregation 的解决方案:

POST /myIndex/_search?typed_keys=true
{
  "query": {
    "query_string": {
      "fields": [
        "title^2",
        "description^2",
        "descriptionOriginal^0.01"
      ],
      "query": "*someSearchTerm*"
    }
  },
  "sort": [{
      "_script": {
        "script": {
          "source": "Math.round(_score / params.fuzziness) * params.fuzziness",
          "params": {
            "fuzziness": 2
          }
        },
        "type": "number",
        "order": "desc"
      }
    }, {
      "_script": {
        "script": {
          "source": "if(doc['catalogId'].value != params.cid) {params.cid=doc['catalogId'].value;params.sort=0;return params.count=0;} else {return (++params.count % params.grpSize == 0) ?++params.sort : params.sort;}",
          "params": {
            "cid": 0,
            "sort": 0,
            "count": 0,
            "grpSize": 3
          }
        },
        "type": "number",
        "order": "asc"
      }
    }, {
      "_score": {
        "order": "desc"
      }
    }
  ]
}

在第一个脚本排序中,我对文档进行了预排序,以便某个 _score-range 内的结果放在一起。这是由模糊参数控制的。然后我使用脚本排序在这些范围内进行排序,以便始终获取每个目录 ID 的下 3 个(由参数 grpSize 控制)文档,然后增加排序顺序。(不知道将脚本参数用作“全局”变量是否危险......我对此感到有点不舒服......)

这是更具可读性的脚本:

if(doc['catalogId'].value != params.cid) {
  params.cid = doc['catalogId'].value;
  params.sort = 0;
  return params.count = 0;
} else {
  return (++params.count % params.grpSize == 0) ? ++params.sort : params.sort;
}

最后但并非最不重要的一点是,具有相同 _score-range 和 sort-order 的文档将按照它们的真实 _score 进行排序。

该解决方案不涉及真正的性能影响(至少在我的索引上)并且提供了我想要的相当结果。

请随时发布想法和优化!

于 2020-01-20T15:13:51.513 回答