帅哥陈 2023-03-01 12:34 采纳率: 0%
浏览 58
已结题

Elasticsearch复杂排序(非script方式)

问题遇到的现象和发生背景

B2B平台涉及到根据不同的会员进行排序,存在Elasticsearch的数据结构大概是这样:

"id":1,
"price":200,
"unitPriceStrategyList" : [
  {  
    "id" : 27,
    "memberPrice" : 500.0,
    "commodityMemberList" : [
      {
        "memberId" : 157,
        "memberRoleId" : 8
      },
      {
        "memberId" : 300,
        "memberRoleId" : 4
      }
    ],
    "isEnable" : true
  },
  {  
    "id" : 28,
    "memberPrice" : 100.0,
    "commodityMemberList" : [
      {
        "memberId" : 152,
        "memberRoleId" : 8
      },
      {
        "memberId" : 298,
    "memberRoleId" : 4
      }
    ],
    "isEnable" : true
  }
]

根据当前登录人,比如是:memberId=157,memberRoleId=8的人进来,就看到500,如果是memberId=152,memberRoleId=8的人进来,就看到100,其他人不在unitPriceStrategyList.commodityMemberList里面的就看到外面的price价格,也就是200,然后根据看到的价格进行升序和降序排序

尝试过的解决方法

用script方式是可以实现这个需求,但是性能太差了,在生产环境压根就不能用

我想要达到的结果

通过Elasticsearch本身的聚合或者重打分实现上面的需求

展开全部

  • 写回答

4条回答 默认 最新

  • MarkHan_ 2023-03-01 12:39
    关注

    该回答引用GPTᴼᴾᴱᴺᴬᴵ
    根据您的需求,您可以考虑使用Elasticsearch中的nested数据类型来存储unitPriceStrategyList中的嵌套结构,以便查询时能够准确匹配。建议将unitPriceStrategyList中的数据展开,每一条数据表示一种商品价格策略,以方便查询。例如,将数据转换成以下格式:

    "id":1,
    "price":200,
    "unitPriceStrategyList" : [
      {  
        "id" : 27,
        "memberId" : 157,
        "memberRoleId" : 8,
        "memberPrice" : 500.0,
        "isEnable" : true
      },
      {  
        "id" : 27,
        "memberId" : 300,
        "memberRoleId" : 4,
        "memberPrice" : 500.0,
        "isEnable" : true
      },
      {  
        "id" : 28,
        "memberId" : 152,
        "memberRoleId" : 8,
        "memberPrice" : 100.0,
        "isEnable" : true
      },
      {  
        "id" : 28,
        "memberId" : 298,
        "memberRoleId" : 4,
        "memberPrice" : 100.0,
        "isEnable" : true
      }
    ]
    
    
    

    接下来,您可以使用nested数据类型来定义unitPriceStrategyList的mapping,如下所示:

    PUT /my_index
    {
      "mappings": {
        "properties": {
          "id": {
            "type": "integer"
          },
          "price": {
            "type": "double"
          },
          "unitPriceStrategyList": {
            "type": "nested",
            "properties": {
              "id": {
                "type": "integer"
              },
              "memberId": {
                "type": "integer"
              },
              "memberRoleId": {
                "type": "integer"
              },
              "memberPrice": {
                "type": "double"
              },
              "isEnable": {
                "type": "boolean"
              }
            }
          }
        }
      }
    }
    
    
    

    接下来,您可以使用nested查询来查询符合条件的数据,并使用nested_path参数来指定unitPriceStrategyList的路径。例如,查询memberId=157和memberRoleId=8的数据,可以使用以下查询:

    GET /my_index/_search
    {
      "query": {
        "nested": {
          "path": "unitPriceStrategyList",
          "query": {
            "bool": {
              "must": [
                {
                  "term": {
                    "unitPriceStrategyList.memberId": 157
                  }
                },
                {
                  "term": {
                    "unitPriceStrategyList.memberRoleId": 8
                  }
                }
              ]
            }
          }
        }
      }
    }
    
    
    

    接下来,您可以使用Elasticsearch的排序功能来对查询结果进行排序。例如,使用以下查询对数据按照memberPrice进行升序排序:

    GET index_name/_search
    {
      "query": {
        "bool": {
          "must": [
            {
              "term": {
                "unitPriceStrategyList.commodityMemberList.memberId": {
                  "value": 157
                }
              }
            },
            {
              "term": {
                "unitPriceStrategyList.commodityMemberList.memberRoleId": {
                  "value": 8
                }
              }
            }
          ]
        }
      },
      "sort": [
        {
          "unitPriceStrategyList.memberPrice": {
            "order": "asc"
          }
        }
      ]
    }
    
    
    

    这个查询使用bool查询来限制memberId和memberRoleId,然后使用sort来按照unitPriceStrategyList.memberPrice进行升序排序。如果需要降序排序,只需要将"order"的值从"asc"改为"desc"即可。

    展开全部

    评论 编辑记录
    帅哥陈 2023-03-01 12:47

    能否给出query语句

    回复
    MarkHan_ 回复 帅哥陈 2023-03-01 13:06

    该回答引用GPTᴼᴾᴱᴺᴬᴵ

    POST /your_index/_search
    {
      "query": {
        "function_score": {
          "query": {
            "match_all": {}
          },
          "functions": [
            {
              "filter": {
                "bool": {
                  "should": [
                    {
                      "term": {
                        "unitPriceStrategyList.commodityMemberList.memberId": 157
                      }
                    },
                    {
                      "term": {
                        "unitPriceStrategyList.commodityMemberList.memberId": 152
                      }
                    }
                  ],
                  "minimum_should_match": 1
                }
              },
              "script_score": {
                "script": {
                  "source": """
                    double memberPrice = 0;
                    for (def item : doc['unitPriceStrategyList']) {
                      for (def member : item.commodityMemberList) {
                        if (member.memberId == params.memberId && member.memberRoleId == params.memberRoleId) {
                          memberPrice = item.memberPrice;
                        }
                      }
                    }
                    if (memberPrice > 0) {
                      return memberPrice;
                    } else {
                      return doc['price'].value;
                    }
                  """,
                  "params": {
                    "memberId": 157,
                    "memberRoleId": 8
                  }
                }
              }
            }
          ],
          "score_mode": "max",
          "boost_mode": "replace"
        }
      },
      "sort": [
        {
          "_score": "desc"
        }
      ]
    }
    
    
    

    这个query语句包含了一个function_score查询,其中的脚本逻辑会根据当前登录人的memberId和memberRoleId,计算出对应的memberPrice。如果memberPrice存在,则使用memberPrice作为排序依据,否则使用price。最后通过_score进行降序排序。

    回复
  • 「已注销」 2023-03-01 12:42
    关注

    参考GPT和自己的思路,可以使用Elasticsearch的function_score查询来实现这个需求,function_score查询可以通过将文档的_score与用户定义的函数结果相结合来重新计算文档的_score,从而影响排序。在这个需求中,我们可以使用function_score查询来重新计算文档的_score,以根据用户的角色和价格选择适当的价格进行排序。

    以下是一个可能的查询示例,它使用function_score查询和nested聚合来实现上述需求:

    {
      "query": {
        "function_score": {
          "query": {
            "match_all": {}
          },
          "functions": [
            {
              "filter": {
                "nested": {
                  "path": "unitPriceStrategyList",
                  "query": {
                    "bool": {
                      "must": [
                        {
                          "term": {
                            "unitPriceStrategyList.isEnable": true
                          }
                        },
                        {
                          "nested": {
                            "path": "unitPriceStrategyList.commodityMemberList",
                            "query": {
                              "bool": {
                                "must": [
                                  {
                                    "term": {
                                      "unitPriceStrategyList.commodityMemberList.memberId": 157
                                    }
                                  },
                                  {
                                    "term": {
                                      "unitPriceStrategyList.commodityMemberList.memberRoleId": 8
                                    }
                                  }
                                ]
                              }
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              },
              "weight": 2
            },
            {
              "filter": {
                "nested": {
                  "path": "unitPriceStrategyList",
                  "query": {
                    "bool": {
                      "must": [
                        {
                          "term": {
                            "unitPriceStrategyList.isEnable": true
                          }
                        },
                        {
                          "nested": {
                            "path": "unitPriceStrategyList.commodityMemberList",
                            "query": {
                              "bool": {
                                "must": [
                                  {
                                    "term": {
                                      "unitPriceStrategyList.commodityMemberList.memberId": 152
                                    }
                                  },
                                  {
                                    "term": {
                                      "unitPriceStrategyList.commodityMemberList.memberRoleId": 8
                                    }
                                  }
                                ]
                              }
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              },
              "weight": 1
            }
          ],
          "score_mode": "sum",
          "boost_mode": "multiply"
        }
      },
      "sort": [
        {
          "_score": {
            "order": "desc"
          }
        },
        {
          "price": {
            "order": "asc"
          }
        }
      ],
      "aggs": {
        "prices": {
          "nested": {
            "path": "unitPriceStrategyList"
          },
          "aggs": {
            "price": {
              "min": {
                "script": "doc['price'].value"
              }
            }
          }
        }
      }
    }
    

    该查询包括以下组件:

    一个function_score查询,其中包括两个函数。第一个函数使用nested查询和bool查询来匹配适当的价格,并使用较高的权重来增加该价格的影响力。第二个函数使用相同的查询来匹配次佳价格,并使用较低的权重增加其影响力。两个函数的结果将相乘以影响排序顺序。

    一个排序,首先按照匹配的价格降序排序,然后按照价格是否匹配以及价格值升序排序。这将使匹配的价格始终排在非匹配价格的前面,并按照价格值升序排序。

    下面是一个可能的Elasticsearch查询示例,假设当前登录用户的memberId为157,memberRoleId为8:

    {
      "query": {
        "function_score": {
          "query": {
            "bool": {
              "should": [
                {
                  "nested": {
                    "path": "unitPriceStrategyList",
                    "score_mode": "sum",
                    "query": {
                      "bool": {
                        "must": [
                          {
                            "range": {
                              "unitPriceStrategyList.memberPrice": {
                                "lte": 10000
                              }
                            }
                          },
                          {
                            "nested": {
                              "path": "unitPriceStrategyList.commodityMemberList",
                              "query": {
                                "bool": {
                                  "must": [
                                    {
                                      "terms": {
                                        "unitPriceStrategyList.commodityMemberList.memberId": [
                                          157
                                        ]
                                      }
                                    },
                                    {
                                      "terms": {
                                        "unitPriceStrategyList.commodityMemberList.memberRoleId": [
                                          8
                                        ]
                                      }
                                    }
                                  ]
                                }
                              }
                            }
                          }
                        ]
                      }
                    },
                    "boost_factor": 2
                  }
                },
                {
                  "nested": {
                    "path": "unitPriceStrategyList",
                    "score_mode": "sum",
                    "query": {
                      "bool": {
                        "must": [
                          {
                            "range": {
                              "unitPriceStrategyList.memberPrice": {
                                "lte": 10000
                              }
                            }
                          },
                          {
                            "nested": {
                              "path": "unitPriceStrategyList.commodityMemberList",
                              "query": {
                                "bool": {
                                  "must": [
                                    {
                                      "terms": {
                                        "unitPriceStrategyList.commodityMemberList.memberId": [
                                          152
                                        ]
                                      }
                                    },
                                    {
                                      "terms": {
                                        "unitPriceStrategyList.commodityMemberList.memberRoleId": [
                                          8
                                        ]
                                      }
                                    }
                                  ]
                                }
                              }
                            }
                          }
                        ]
                      }
                    },
                    "boost_factor": 0.5
                  }
                }
              ]
            }
          },
          "functions": [
            {
              "field_value_factor": {
                "field": "unitPriceStrategyList.memberPrice",
                "modifier": "log1p",
                "factor": 2
              }
            }
          ],
          "score_mode": "multiply"
        }
      },
      "sort": [
        {
          "_score": {
            "order": "desc"
          }
        },
        {
          "price": {
            "order": "asc"
          }
        }
      ]
    }
    

    在这个查询中,我们添加了一个嵌套查询,用于匹配次佳价格。我们使用相同的嵌套路径和布尔查询来匹配 memberRoleId=8 和 memberId=152 的价格。我们使用了一个较低的 boost_factor 来减小次佳价格的影响力。

    最后,我们将两个函数的结果相乘以影响排序顺序,然后使用 _score 和价格字段进行排序。

    希望这可以帮助你解决你的问题!

    展开全部

    评论 编辑记录
  • 极客智能体-在线 2023-03-03 09:40
    关注

    以下答案由GPT-3.5大模型与博主波罗歌共同编写:
    可以通过Elasticsearch中的function_score query来实现这个需求,具体的步骤如下:

    1. 使用bool query把不在unitPriceStrategyList.commodityMemberList里面的文档和在里面但是会员价格为null的文档过滤掉。

    2. 使用function_score query对剩下的文档进行重打分操作。

    3. 在重打分操作中,使用script_score进行自定义脚本计算,根据当前登录人的memberId和memberRoleId计算出当前会员价格。

    4. 然后使用field_value_factor将计算出来的会员价格作为因子进行计算,达到根据会员价格进行排序的目的。

    具体实现代码如下:

    POST index/_search
    {
      "query": {
        "bool": {
          "filter": [
            {
              "bool": {
                "should": [
                  {
                    "nested": {
                      "path": "unitPriceStrategyList",
                      "query": {
                        "bool": {
                          "must": [
                            {
                              "term": {
                                "unitPriceStrategyList.commodityMemberList.memberId": 157
                              }
                            },
                            {
                              "term": {
                                "unitPriceStrategyList.commodityMemberList.memberRoleId": 8
                              }
                            }
                          ]
                        }
                      }
                    }
                  },
                  {
                    "bool": {
                      "must_not": [
                        {
                          "nested": {
                            "path": "unitPriceStrategyList",
                            "query": {
                              "bool": {
                                "must": [
                                  {
                                    "exists": {
                                      "field": "unitPriceStrategyList.commodityMemberList.memberRoleId"
                                    }
                                  },
                                  {
                                    "term": {
                                      "unitPriceStrategyList.commodityMemberList.memberId": 157
                                    }
                                  }
                                ]
                              }
                            }
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          ]
        }
      },
      "sort": [
        {
          "_score": {
            "order": "desc"
          }
        },
        {
          "price": {
            "order": "desc"
          }
        }
      ],
      "size": 10,
      "from": 0, 
      "track_total_hits": true,
      "explain": true,
      "min_score": 0
      "rescore": {
        "window_size": 1000,
        "query": {
          "rescore_query": {
            "function_score": {
              "query": {"match_all": {}},
              "script_score": {
                "script": {
                  "source": """
                    def price = 0;
                    boolean flag = false;
                    for (def i = 0; i < doc['unitPriceStrategyList.commodityMemberList.memberId'].length; i++) {
                      if (doc['unitPriceStrategyList.commodityMemberList.memberId'][i].value == params.memberId &&
                          doc['unitPriceStrategyList.commodityMemberList.memberRoleId'][i].value == params.memberRoleId
                      ) {
                          if (doc['unitPriceStrategyList.memberPrice'].length > i && doc['unitPriceStrategyList.memberPrice'][i].value != null) {
                              price = doc['unitPriceStrategyList.memberPrice'][i].value;
                              flag = true;
                              break;
                          }
                      }
                    }
                    if (flag) {
                        return price;
                    } else {
                        return doc['price'].value;
                    }
                  """,
                  "params": {
                    "memberId": 157,
                    "memberRoleId": 8
                  }
                }
              },
              "field_value_factor": {
                "field": "memberPrice",
                "factor": 1,
                "modifier": "log1p",
                "missing": 0
              },
              "boost_mode": "multiply"
            }
          }
        }
      }
    }
    

    这里使用了嵌套查询(nested query)来查询unitPriceStrategyList中的文档,然后使用function_score query来进行重打分操作,其中包含自定义脚本计算和field_value_factor计算因子。最后在sort中根据_score和price进行排序。
    如果我的回答解决了您的问题,请采纳!

    展开全部

    评论
  • CodeBytes 2023-03-01 12:37
    关注

    该回答引用ChatGPT

    您可以使用Elasticsearch中的Function Score Query实现根据不同的会员进行排序。该查询可以通过定义一个或多个函数,以一种灵活的方式修改文档的分数,从而影响搜索结果的排序。

    在这种情况下,您可以使用Script Score Function和Field Value Factor Function两个函数来实现您的需求。Script Score Function将通过比较当前登录人的ID和角色ID与unitPriceStrategyList中的commodityMemberList中的成员ID和角色ID来计算文档的得分。Field Value Factor Function将使用文档中的价格作为因子来调整得分。

    以下是使用Function Score Query的示例查询:

    
    {
      "query": {
        "function_score": {
          "query": {
            "match_all": {}
          },
          "functions": [
            {
              "script_score": {
                "script": {
                  "source": """
                  def member_id = params.member_id;
                  def member_role_id = params.member_role_id;
                  def unit_price_strategy_list = doc['unitPriceStrategyList'];
                  
                  def max_price = doc['price'].value;
                  
                  for (def i = 0; i < unit_price_strategy_list.length; i++) {
                    def strategy = unit_price_strategy_list[i];
                    
                    for (def j = 0; j < strategy['commodityMemberList'].length; j++) {
                      def member = strategy['commodityMemberList'][j];
                      
                      if (member['memberId'] == member_id && member['memberRoleId'] == member_role_id) {
                        return strategy['memberPrice'];
                      }
                    }
                    
                    if (strategy['isEnable'] && strategy['memberPrice'] > max_price) {
                      max_price = strategy['memberPrice'];
                    }
                  }
                  
                  return max_price;
                  """,
                  "params": {
                    "member_id": 157,
                    "member_role_id": 8
                  }
                }
              }
            },
            {
              "field_value_factor": {
                "field": "price",
                "modifier": "log1p",
                "factor": 0.1
              }
            }
          ],
          "boost_mode": "multiply"
        }
      }
    }
    
    

    在这个查询中,我们使用了两个函数:Script Score Function和Field Value Factor Function。Script Score Function首先查找unitPriceStrategyList中与当前登录用户匹配的成员,并返回该成员的价格。如果没有匹配的成员,则返回最高价格。Field Value Factor Function将价格作为因子应用于得分,并使用log1p修改因子的影响,以避免价格因子对得分的影响过大。

    请注意,此查询只是示例代码,并未经过测试和优化,因此可能需要根据您的具体需求进行调整和修改。

    展开全部

    评论
    帅哥陈 2023-03-01 12:42

    说了Script Score性能太差,不能用

    回复
    CodeBytes 回复 帅哥陈 2023-03-01 12:48

    以下是使用Nested类型和Inner Hits功能的示例查询:

    {
      "query": {
        "nested": {
          "path": "unitPriceStrategyList",
          "query": {
            "bool": {
              "should": [
                {
                  "bool": {
                    "must": [
                      {
                        "term": {
                          "unitPriceStrategyList.commodityMemberList.memberId": 157
                        }
                      },
                      {
                        "term": {
                          "unitPriceStrategyList.commodityMemberList.memberRoleId": 8
                        }
                      }
                    ]
                  }
                },
                {
                  "bool": {
                    "must_not": [
                      {
                        "nested": {
                          "path": "unitPriceStrategyList.commodityMemberList",
                          "query": {
                            "bool": {
                              "must": [
                                {
                                  "term": {
                                    "unitPriceStrategyList.commodityMemberList.memberId": 157
                                  }
                                },
                                {
                                  "term": {
                                    "unitPriceStrategyList.commodityMemberList.memberRoleId": 8
                                  }
                                }
                              ]
                            }
                          }
                        }
                      }
                    ]
                  }
                }
              ]
            }
          },
          "inner_hits": {
            "name": "matched_commodity_members",
            "size": 1,
            "docvalue_fields": [
              {
                "field": "unitPriceStrategyList.memberPrice",
                "format": "float"
              }
            ]
          }
        }
      },
      "sort": [
        {
          "_score": {
            "order": "desc"
          }
        }
      ]
    }
    
    

    在这个查询中,我们使用了Nested类型来处理unitPriceStrategyList字段。在查询期间,我们使用should查询来查找与当前登录用户匹配的成员。如果有匹配的成员,则将相应的文档加入结果集中,并使用Inner Hits功能获取匹配的成员的价格。如果没有匹配的成员,则使用最高价格。最后,我们对结果进行排序,以便按得分降序排列。

    回复
    帅哥陈 回复 CodeBytes 2023-03-01 13:06

    不对吧,没跟最外层的price进行比较排序

    回复
    展开全部4条评论
编辑
预览

报告相同问题?

问题事件

  • 系统已结题 3月8日
  • 创建了问题 3月1日
手机看
程序员都在用的中文IT技术交流社区

程序员都在用的中文IT技术交流社区

专业的中文 IT 技术社区,与千万技术人共成长

专业的中文 IT 技术社区,与千万技术人共成长

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

客服 返回
顶部