Elasticsearch - 通用构面结构 - 计算与过滤器结合的聚合

时间:2016-12-28 21:10:29

标签: elasticsearch aggregate facet

在我们的一个新项目中,我们受到了这篇文章http://project-a.github.io/on-site-search-design-patterns-for-e-commerce/#generic-faceted-search的启发,因为它们是我们的“方面”结构。虽然我已经在文章描述的范围内使用它,但是在选择方面时我遇到了让它工作的问题。我希望有人能给出一些尝试的提示,所以我不必再将所有聚合重做为单独的聚合计算。

问题基本上是我们使用单个聚合一次计算所有“方面”,但是当我添加过滤器(fx。检查品牌名称)时,它会在返回时“删除”所有其他品牌聚合。我基本上想要的是它在计算其他方面时应该使用该品牌作为过滤器,但在计算品牌聚合时则不然。这是必要的,因此用户可以选择多个品牌。

https://www.contorion.de/search/Metabo_Fein/ou1-ou2?q=Winkelschleifer&c=bovy(这是上面文章中描述的网站),我选择了“Metabo”和“Fein”制造商(Hersteller),展开了Hersteller菜单,它显示了所有制造商,而不仅仅是选中的。所以我知道它有可能以某种方式,我希望有一个关于如何编写聚合/过滤器的提示,所以我得到了“正确的电子商务方面行为”。

在ES中的产品上,我有以下结构:(与原始文章相同,但在命名时使用“C#”ified)

"attributeStrings": [
    {
        "facetName": "Property",
        "facetValue": "Organic"
    },
    {
        "facetName": "Property",
        "facetValue": "Without parfume"
    },
    {
        "facetName": "Brand",
        "facetValue": "Adidas"
    }
]

所以上面的产品有2个属性/方面组 - 具有2个值的属性(有机,没有香水)和带有1个值的品牌(阿迪达斯)。 没有任何过滤器,我会根据以下查询计算聚合:

  "aggs": {
    "agg_attr_strings_filter": {
      "filter": {},
      "aggs": {
        "agg_attr_strings": {
          "nested": {
            "path": "attributeStrings"
          },
          "aggs": {
            "attr_name": {
              "terms": {
                "field": "attributeStrings.facetName"
              },
              "aggs": {
                "attr_value": {
                  "terms": {
                    "field": "attributeStrings.facetValue",
                    "size": 1000,
                    "order": [
                      {
                        "_term": "asc"
                      }
                    ]
   } } } } } } } }

现在,如果我选择Property“Organic”和Brand“Adidas”,我会构建相同的聚合,但是使用过滤器来应用这两个约束(这有点出错......):

  "aggs": {
    "agg_attr_strings_filter": {
      "filter": {
        "bool": {
          "filter": [
            {
              "nested": {
                "query": {
                  "bool": {
                    "filter": [
                      {
                        "term": {
                          "attributeStrings.facetName": {
                            "value": "Property"
                          }
                        }
                      },
                      {
                        "terms": {
                          "attributeStrings.facetValue": [
                            "Organic"
                          ]
                        }
                      }
                    ]
                  }
                },
                "path": "attributeStrings"
              }
            },
            {
              "nested": {
                "query": {
                  "bool": {
                    "filter": [
                      {
                        "term": {
                          "attributeStrings.facetName": {
                            "value": "Brand"
                          }
                        }
                      },
                      {
                        "terms": {
                          "attributeStrings.facetValue": [
                            "Adidas"
                          ]
                        }
                      }
                    ]
                  }
                },
                "path": "attributeStrings"
              }
            }
          ]
        }
      },
      "aggs": {
        "agg_attr_strings": {
          "nested": {
            "path": "attributeStrings"
          },
          "aggs": {
            "attr_name": {
              "terms": {
                "field": "attributeStrings.facetName",
              },
              "aggs": {
                "attr_value": {
                  "terms": {
                    "field": "attributeStrings.facetValue",
                    "size": 1000,
                    "order": [
                      {
                        "_term": "asc"
                      }
                    ]
   } } } } } } } }

我能看到这个模型的唯一方法是计算每个选定方面的聚合,并以某种方式合并结果。但它似乎非常复杂,有点像文章中所描述的那样使模型失败,所以我希望有一个更清晰的解决方案,有人可以提供一些尝试。

3 个答案:

答案 0 :(得分:16)

  

我能看到这个模型的唯一方法是计算每个选定方面的聚合,并以某种方式合并结果。

这是完全正确的。如果您选择了一个方面(例如品牌),那么如果您还想要获取其他品牌进行多选,则无法使用全球品牌过滤器。您可以执行的操作是在所选方面应用所有其他过滤器,在非选定方面应用所有 过滤器。作为结果,您将为n+1个选定过滤器提供n个单独的聚合 - 第一个用于所有方面,其余用于选定方面。

在您的情况下,查询可能如下所示:

{
  "aggs": {
    "agg_attr_strings_filter": {
      "filter": {
        "bool": {
          "filter": [
            {
              "nested": {
                "query": {
                  "bool": {
                    "filter": [
                      {
                        "term": {
                          "attributeStrings.facetName": {
                            "value": "Property"
                          }
                        }
                      },
                      {
                        "terms": {
                          "attributeStrings.facetValue": [
                            "Organic"
                          ]
                        }
                      }
                    ]
                  }
                },
                "path": "attributeStrings"
              }
            },
            {
              "nested": {
                "query": {
                  "bool": {
                    "filter": [
                      {
                        "term": {
                          "attributeStrings.facetName": {
                            "value": "Brand"
                          }
                        }
                      },
                      {
                        "terms": {
                          "attributeStrings.facetValue": [
                            "Adidas"
                          ]
                        }
                      }
                    ]
                  }
                },
                "path": "attributeStrings"
              }
            }
          ]
        }
      },
      "aggs": {
        "agg_attr_strings": {
          "nested": {
            "path": "attributeStrings"
          },
          "aggs": {
            "attr_name": {
              "terms": {
                "field": "attributeStrings.facetName"
              },
              "aggs": {
                "attr_value": {
                  "terms": {
                    "field": "attributeStrings.facetValue",
                    "size": 1000,
                    "order": [
                      {
                        "_term": "asc"
                      }
                    ]
                  }
                }
              }
            }
          }
        }
      }
    },
    "special_agg_property": {
      "filter": {
        "nested": {
          "query": {
            "bool": {
              "filter": [
                {
                  "term": {
                    "attributeStrings.facetName": {
                      "value": "Brand"
                    }
                  }
                },
                {
                  "terms": {
                    "attributeStrings.facetValue": [
                      "Adidas"
                    ]
                  }
                }
              ]
            }
          },
          "path": "attributeStrings"
        }
      },
      "aggs": {
        "special_agg_property": {
          "nested": {
            "path": "attributeStrings"
          },
          "aggs": {
            "agg_filtered_special": {
              "filter": {
                "query": {
                  "match": {
                    "attributeStrings.facetName": "Property"
                  }
                }
              },
              "aggs": {
                "facet_value": {
                  "terms": {
                    "size": 1000,
                    "field": "attributeStrings.facetValue"
                  }
                }
              }
            }
          }
        }
      }
    },
    "special_agg_brand": {
      "filter": {
        "nested": {
          "query": {
            "bool": {
              "filter": [
                {
                  "term": {
                    "attributeStrings.facetName": {
                      "value": "Property"
                    }
                  }
                },
                {
                  "terms": {
                    "attributeStrings.facetValue": [
                      "Organic"
                    ]
                  }
                }
              ]
            }
          },
          "path": "attributeStrings"
        }
      },
      "aggs": {
        "special_agg_brand": {
          "nested": {
            "path": "attributeStrings"
          },
          "aggs": {
            "agg_filtered_special": {
              "filter": {
                "query": {
                  "match": {
                    "attributeStrings.facetName": "Brand"
                  }
                }
              },
              "aggs": {
                "facet_value": {
                  "terms": {
                    "size": 1000,
                    "field": "attributeStrings.facetValue"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

这个查询看起来超级大而可怕,但生成这样的查询可以用几十行代码完成。 解析查询结果时,需要首先解析一般聚合(使用所有过滤器的聚合)和特殊构面聚合之后。从上面的示例中,首先解析来自agg_attr_strings_filter的结果,但这些结果还将包含 Brand Property 的聚合值,这些聚合值应该由来自{{的聚合值覆盖1}}和special_agg_property 此外,此查询是有效的,因为Elasticsearch在缓存单独的过滤器子句方面做得很好,因此在查询的不同部分应用相同的过滤器应该很便宜。

  

但它看起来非常复杂,有点像文章中所描述的那样使模型失败,所以我希望有一个更清晰的解决方案,有人可以提供一些尝试。

实际上,您无需将不同的过滤器应用于不同的方面,同时具有不同的查询过滤器。如果您需要支持"纠正电子商务方面的行为"你将有复杂的查询:)

免责声明:我是上述文章的合着者。

答案 1 :(得分:5)

问题来自于您在聚合中添加PropertyOrganic 过滤器的事实,因此您选择的方面越多,您就越能克制您将获得的条款。在该文章中,他们使用的filter实际上是post_filter,这两个名称直到最近才被允许,但filter got removed因为这导致含糊不清。

您需要做的是将聚合外部的过滤器移动到post_filter部分,以便通过拾取的任何方面正确滤除结果,但所有方面仍然可以在整个文件集。

{
  "post_filter": {
    "bool": {
      "filter": [
        {
          "nested": {
            "query": {
              "bool": {
                "filter": [
                  {
                    "term": {
                      "attributeStrings.facetName": {
                        "value": "Property"
                      }
                    }
                  },
                  {
                    "terms": {
                      "attributeStrings.facetValue": [
                        "Organic"
                      ]
                    }
                  }
                ]
              }
            },
            "path": "attributeStrings"
          }
        },
        {
          "nested": {
            "query": {
              "bool": {
                "filter": [
                  {
                    "term": {
                      "attributeStrings.facetName": {
                        "value": "Brand"
                      }
                    }
                  },
                  {
                    "terms": {
                      "attributeStrings.facetValue": [
                        "Adidas"
                      ]
                    }
                  }
                ]
              }
            },
            "path": "attributeStrings"
          }
        }
      ]
    }
  },
  "aggs": {
    "agg_attr_strings_full": {
      "nested": {
        "path": "attributeStrings"
      },
      "aggs": {
        "attr_name": {
          "terms": {
            "field": "attributeStrings.facetName"
          },
          "aggs": {
            "attr_value": {
              "terms": {
                "field": "attributeStrings.facetValue",
                "size": 1000,
                "order": [
                  {
                    "_term": "asc"
                  }
                ]
              }
            }
          }
        }
      }
    },
    "agg_attr_strings_filtered": {
      "filter": {
        "bool": {
          "filter": [
            {
              "nested": {
                "path": "attributeStrings",
                "query": {
                  "bool": {
                    "filter": [
                      {
                        "term": {
                          "attributeStrings.facetName": {
                            "value": "Property"
                          }
                        }
                      },
                      {
                        "terms": {
                          "attributeStrings.facetValue": [
                            "Organic"
                          ]
                        }
                      }
                    ]
                  }
                }
              }
            },
            {
              "nested": {
                "path": "attributeStrings",
                "query": {
                  "bool": {
                    "filter": [
                      {
                        "term": {
                          "attributeStrings.facetName": {
                            "value": "Brand"
                          }
                        }
                      },
                      {
                        "terms": {
                          "attributeStrings.facetValue": [
                            "Adidas"
                          ]
                        }
                      }
                    ]
                  }
                }
              }
            }
          ]
        }
      },
      "aggs": {
        "nested": {
          "path": "attributeStrings"
        },
        "aggs": {
          "attr_name": {
            "terms": {
              "field": "attributeStrings.facetName"
            },
            "aggs": {
              "attr_value": {
                "terms": {
                  "field": "attributeStrings.facetValue",
                  "size": 1000,
                  "order": [
                    {
                      "_term": "asc"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}

答案 2 :(得分:0)

我正在努力实现你们在这里讨论的内容。我也关注该线程中提到的文章,但是尝试了3个星期之后,我似乎无法弄清楚过滤器为何返回正确的结果,但是当我将过滤器放入聚合中时,聚合都是错误的。我已经用示例in this question详细介绍了整个过程,我想知道是否有人对我有任何帮助,以防我陷入困境。非常感谢!