【问题标题】:Elastic synonym usage in aggregations聚合中的弹性同义词使用
【发布时间】:2025-12-19 21:35:15
【问题描述】:

情况

使用的弹性版本:2.3.1

我有一个像这样配置的弹性索引

PUT /my_index
{
  "settings": {
    "analysis": {
      "filter": {
        "my_synonym_filter": {
          "type": "synonym", 
          "synonyms": [ 
            "british,english",
            "queen,monarch"
          ]
        }
      },
      "analyzer": {
        "my_synonyms": {
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "my_synonym_filter" 
          ]
        }
      }
    }
  }
}

这很好,当我查询文档并使用查询词“english”或“queen”时,我会得到所有与 british 匹配的文档> 和君主。当我在过滤器聚合中使用同义词时,它不起作用。例如

在我的索引中,我有 5 个文档,其中 3 个有 monarch,其中 2 个有 Queen

POST /my_index/_search
{
  "size": 0,
  "query" : {
      "match" : {
         "status.synonym":{
            "query": "queen",
            "operator": "and"
         }
      }
   },
     "aggs" : {
        "status_terms" : {
            "terms" : { "field" : "status.synonym" }
        },
        "monarch_filter" : {
            "filter" : { "term": { "status.synonym": "monarch" } }
        }
    },
   "explain" : 0
}

结果产生:

总点击量:

  • 5 文档计数(正如预期的那样,太棒了!)
  • 状态条款:女王的 5 个文档计数(正如预期的那样,太棒了!)
  • Monarch 过滤器:0 文档计数

我尝试了不同的同义词过滤器配置:

  • 王后,君主
  • 女王,君主 => 女王
  • 女王,君主 => 女王,君主

但上述并没有改变结果。我想得出结论,也许您只能在查询时使用过滤器,但是如果术语聚合有效,为什么不应该过滤,因此我认为我的同义词过滤器配置是错误的。可以找到更广泛的同义词过滤器示例here

问题

如何在过滤聚合中使用/配置同义词?

复制上述案例的示例: 1. 创建和配置索引:

PUT /my_index
{
  "settings": {
    "analysis": {
      "filter": {
        "my_synonym_filter": {
          "type": "synonym",
          "synonyms": [
            "wlh,wellhead=>wellwell"
          ]
        }
      },
      "analyzer": {
        "my_synonyms": {
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "my_synonym_filter"
          ]
        }
      }
    }
  }
}

PUT my_index/_mapping/job
{
  "properties": {
    "title":{
      "type": "string",
      "analyzer": "my_synonyms"
    }
  }
}

2.放两个文件:

PUT my_index/job/1
{
    "title":"wellhead smth else"
}

PUT my_index/job/2
{
    "title":"wlh other stuff"
}

3.对 wlh 执行搜索,应该返回 2 个文档;有一个术语聚合,它应该有 2 个 wellwell 文档和一个不应该有 0 计数的过滤器:

POST my_index/_search
{
  "size": 0,
  "query" : {
      "match" : {
         "title":{
            "query": "wlh",
            "operator": "and"
         }
      }
   },
     "aggs" : {
        "wlhAggs" : {
            "terms" : { "field" : "title" }
        },
        "wlhFilter" : {
            "filter" : { "term": { "title": "wlh"     } }
        }
    },
   "explain" : 0
}

这个查询的结果是:

   {
   "took": 8,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 2,
      "max_score": 0,
      "hits": []
   },
   "aggregations": {
      "wlhAggs": {
         "doc_count_error_upper_bound": 0,
         "sum_other_doc_count": 0,
         "buckets": [
            {
               "key": "wellwell",
               "doc_count": 2
            },
            {
               "key": "else",
               "doc_count": 1
            },
            {
               "key": "other",
               "doc_count": 1
            },
            {
               "key": "smth",
               "doc_count": 1
            },
            {
               "key": "stuff",
               "doc_count": 1
            }
         ]
      },
      "wlhFilter": {
         "doc_count": 0
      }
   }
}

这就是我的问题,wlhFilter 中应该至少有 1 个文档计数。

【问题讨论】:

    标签: elasticsearch filter analyzer synonym


    【解决方案1】:

    我的时间很短,所以如果需要,我可以在今天/明天晚些时候再详细说明。但以下应该有效:

    DELETE /my_index
    PUT /my_index
    {
      "settings": {
        "analysis": {
          "filter": {
            "my_synonym_filter": {
              "type": "synonym", 
              "synonyms": [ 
                "british,english",
                "queen,monarch"
              ]
            }
          },
          "analyzer": {
            "my_synonyms": {
              "tokenizer": "standard",
              "filter": [
                "lowercase",
                "my_synonym_filter" 
              ]
            }
          }
        }
      },
      "mappings": {
        "test": {
          "properties": {
            "title": {
              "type": "text",
              "analyzer": "my_synonyms",
              "fielddata": true
            }
          }
        }
      }
    }
    POST my_index/test/1
    {
      "title" : "the british monarch"
    }
    
    GET my_index/_search
    {
      "query": {
        "match": {
          "title": "queen"
        }
      }
    }
    
    GET my_index/_search
    {
      "query": {
        "match": {
          "title": "queen"
        }
      }, 
      "aggs": {
        "queen_filter": {
          "filter": {
            "term": {
              "title": "queen"
            }
          }
        },
        "monarch_filter": {
          "filter": {
            "term": {
              "title": "monarch"
            }
          }
        }
      }
    }
    

    能否分享您为status.synonym 字段定义的映射?

    编辑:V2

    您的过滤器输出为 0 的原因是因为 Elasticsearch 中的过滤器从不经过分析阶段。它适用于精确匹配。

    聚合中的标记“wlh”不会被转换为“wellwell”,这意味着它不会出现在倒排索引中。这是因为,在索引期间,您的“wlh”被翻译成“wellwell”。 为了达到您想要的效果,您必须将数据索引到一个单独的字段中并相应地调整您的过滤器。

    你可以试试这样的:

    DELETE my_index
    PUT /my_index
    {
      "settings": {
        "number_of_shards": 1, 
        "number_of_replicas": 0, 
        "analysis": {
          "filter": {
            "my_synonym_filter": {
              "type": "synonym",
              "synonyms": [
                "wlh,wellhead=>wellwell"
              ]
            }
          },
          "analyzer": {
            "my_synonyms": {
              "tokenizer": "standard",
              "filter": [
                "lowercase",
                "my_synonym_filter"
              ]
            }
          }
        }
      },
      "mappings": {
        "job": {
          "properties": {
            "title": {
              "type": "string",
              "fields": {
                "synonym": {
                  "type": "string",
                  "analyzer": "my_synonyms"
                }
              }
            }
          }
        }
      }
    }
    
    PUT my_index/job/1
    {
        "title":"wellhead smth else"
    }
    
    PUT my_index/job/2
    {
        "title":"wlh other stuff"
    }
    
    POST my_index/_search
    {
      "size": 0,
      "query": {
        "match": {
          "title.synonym": {
            "query": "wlh",
            "operator": "and"
          }
        }
      },
      "aggs": {
        "wlhAggs": {
          "terms": {
            "field": "title.synonym"
          }
        },
        "wlhFilter": {
          "filter": {
            "term": {
              "title": "wlh"
            }
          }
        }
      }
    }
    

    输出:

    {
      "aggregations": {
        "wlhAggs": {
          "doc_count_error_upper_bound": 0,
          "sum_other_doc_count": 0,
          "buckets": [
            {
              "key": "wellwell",
              "doc_count": 2
            },
            {
              "key": "else",
              "doc_count": 1
            },
            {
              "key": "other",
              "doc_count": 1
            },
            {
              "key": "smth",
              "doc_count": 1
            },
            {
              "key": "stuff",
              "doc_count": 1
            }
          ]
        },
        "wlhFilter": {
          "doc_count": 1
        }
      }
    }
    

    希望这会有所帮助!

    【讨论】:

    • 我猜关键位是“fielddata”:是的。如果是这样的话,我很想知道为什么同义词字段术语聚合在没有该设置的情况下工作并且过滤一个不。需要查看如何使用 NEST 设置字段数据选项,因为据我所知,没有直接的方法来设置 true 标志。感谢收获,会回来找你的
    • 如果您能给我看一下您的同义词字段的映射,我们就能知道出了什么问题。看起来您正在使用的过滤器聚合可能针对未分析的字段。如果这是真的,那么 ES 将不会在倒排索引中存储“女王”,因为它没有使用同义词分析器进行分析。我有 fielddata=true 的原因是因为(没有设置)不可能在“文本”字段上进行聚合。这就是为什么您似乎在聚合一个无法解析同义词的字段:)
    • 我添加了一个包含示例代码的部分,您可以使用它来复制我的问题。我应该真正开始这样做,以使问题更清晰,但不要混淆我不会编辑问题部分的东西。此外,ES 版本是 2.3.1,我无法复制您的示例,因为文本直到更高版本才成为属性/功能。当然,如果需要进行版本升级以使其正常工作,那不是问题,但如果这是“最后的手段”,我更喜欢。
    • 啊,我假设您使用的是 ES 5,因为您的问题中有 elasticsearch-5 标签 :) 我会看看它并回复您!
    • es-5 标签不是故意的,现在才注意到,但因为它引起了你的注意,我不后悔犯了这个错误!无论如何,这很好,但我的问题仍然存在 - 我怎样才能让过滤器聚合检测两个文档?例如,两个文档可能被索引为“wlh”,但过滤器可能正在寻找“井口”,这将产生 0 的计数,但是因为它们都是同义词,我想它应该返回 2 的计数。或者因为上面的原因是不可能的?这会在 ES 5.x 中工作吗?我可以为我的过滤器聚合编写单独的匹配查询并获取总数
    【解决方案2】:

    所以在下面@Byron Voorbach 和他的 cmets 的帮助下,这是我的解决方案:

    • 我创建了一个单独的字段,我在上面使用同义词分析器,如 反对拥有一个属性字段 (mainfield.property)。
    • 最重要的问题是我的同义词被压缩了!我 有,例如,英国,英语=>英国。将其更改为 英国,英语,英国解决了我的问题,过滤器聚合是 返回正确数量的文档。

    希望这对某人有所帮助,或者至少指向正确的方向。

    编辑: 哦,上帝赞美文档!我完全解决了我的过滤器(S!)聚合问题(链接here)。在过滤器配置中,我指定了匹配类型的查询并且它有效!最终得到这样的结果:

    "aggs" : {
        "messages" : {
          "filters" : {
            "filters" : {
              "status" :   { "match" : { "cats.saurus" : "monarch"   }},
              "country" : { "match" : { "cats.saurus" : "british" }}
            }
          }
        }
      }
    

    【讨论】:

    • 很高兴你知道了!
    • 我同时发布了更新..找到了更好的解决方案。感谢您的帮助