Elasticsearch按儿童排序

时间:2016-09-28 13:01:54

标签: elasticsearch

两个实体:集合和产品。 Collection是产品的母公司。

我需要按产品的条款进行搜索,并展示每个产品有4种产品。

收藏品和产品可以部分匹配,但最先匹配。如果匹配不满,则某些条款具有优先权。

示例:搜索"颜色:红色"和"材料:石头"需要首先显示红色宝石,然后显示任何其他红色(这是关于集合匹配和产品匹配)。

所以,所有这些都是通过以下要求解决的:

{
  "query": {
    "has_child": {
      "type": "products",
      "query": {
        "bool": {
          "should": [
            {
              "constant_score": {
                "filter": {
                  "match_all": {}
                },
                "boost": 1
              }
            },
            {
              "constant_score": {
                "filter": {
                  "terms": { "_name": "colors", "colors": [5] }
                },
                "boost": 1.2
              }
            },
            {
              "constant_score": {
                "filter": {
                  "terms": { "_name": "materials", "productTypes": [6] }
                },
                "boost": 1
              }
            }
          ]
        }
      },
      "score_mode": "max",
      "inner_hits": {
        "size": 4,
        "sort": [
          "_score"
        ]
      }
    }
  },
  "sort": [
    "_score"
  ]
}

好的,现在麻烦了。

需要按价格排序。作为ASC,作为DESC。价格是产品的财产。

需要按匹配产品的价格进行排序,因此无法将价格转移到收藏中。需要按价格排序作为产品的集合。按照匹配产品的最小(或最大)价格排序的集合。

需要按价格排序只有100%匹配的产品(好吧,部分匹配也可以排序,但之后)。我的意思是,排序必须像ORDER BY _score,price

示例,我希望得到,按价格排序asc,[nn]表示部分匹配的产品:

Collection1
100 - 200 - 800 - [99]
Collection2
300 - 500 - [10] - [20]
Collection3
400 - 450 - 500 - [100]

我发现按孩子排序is not supported。并建议重新计算得分。但是我按比赛分数使用得分。 我的尝试是

{
  "query": {
    "has_child": {
      "type": "products",
      "query": {
        "function_score": {
          "query": {
            "bool": {
              "should": [
                ... same query as above ...
              ]
            }
          },
          "functions": [
            {
              "script_score": {
                "script": "ceil(_score * 100) * 100000 + (99999 - doc['price'].value/100)",
                "lang": "expression"
              }
            }
          ]
        }
      },
      "score_mode": "max",
      "inner_hits": {
        "size": 4,
        "sort": [
          "_score",
          {
            "price": {
              "order": "desc"
            }
          }
        ]
      }
    }
  },
  "sort": [
    "_score"
  ]
}

但我对结果得分感到困惑,我可以在回答中看到。寻求帮助:)或者,可能是,删除它并创建嵌套索引?

UPD:发现分数有问题。默认情况下,弹性组合得分和script_score的结果。因此得分为ceil(_score * 100) * 100000 + (99999 - doc['price'].value/100) * _score - 这可能会破坏想法,但很容易使用boost_mode function_score参数进行修复。结果查询:

{
  "query": {
    "has_child": {
      "type": "products",
      "query": {
        "function_score": {
          "query": {
            "bool": {
              "should": [
                ... same query as above ...
              ]
            }
          },
          "functions": [
            {
              "script_score": {
                "script": "ceil((log10(_score)+10) * 100) * 100000 + (99999 - doc['price'].value)",
                "lang": "expression"
              }
            }
          ],
          "boost_mode": "replace"
        }
      },
      "score_mode": "max",
      "inner_hits": {
        "size": 4,
        "sort": [
          "_score",
          {
            "price": {
              "order": "desc"
            }
          }
        ]
      }
    }
  },
  "sort": [
    "_score"
  ]
}

boost_mode == 'replace表示"使用功能结果作为得分"。另外,使用log10来确定_score中有多少位数。 按价格排序DESC需要将公式更改为ceil((log10(_score)+10) * 100) * 100000 + (doc['price'].value)

UPD2

公式ceil((log10(_score)+10) * 100) * 100000 + (99999 - doc['price'].value)返回价格100099952的{​​{1}}和价格48(boost == 1,queryNorm == 1),因为单精度限制。

新公式50 - 减少了得分的位数,并从价格转换为lg价格和减少了位数。欢迎反馈。

1 个答案:

答案 0 :(得分:0)

感谢分享,将最新的公式更新为ceil((log10(_score+1)+5) * 100) * 10000 + (9999 - ceil(log10(doc['price'].value +1) * 1000)),使+1得分,因为在某些情况下它会返回如下错误:

 "function score query returned an invalid score: -Infinity for doc: 4580"

更新:出现另一个错误:

 "function score query returned an invalid score: NaN for doc: 1739"

将公式更改为ceil((log10(_score+1)+5) * 100) * 10000 + (9999 - ceil(log10(doc['price'].value +1) * 1000)),为文档值添加了+1,以解决此问题

更新2 :出现另一个错误:

 "function score query returned an invalid score: NaN for doc: 1739"

将公式更改为ceil((log10(_score+1)+5) * 100) * 10000 + (9999 - ceil(log10(doc['price'].value > 0 ? doc['price'].value : 1) * 1000)),将+1替换为表达式