【问题标题】:Immense term error in elasticsearch弹性搜索中的巨大术语错误
【发布时间】:2015-09-05 01:43:11
【问题描述】:

我正在开发一个会员管理计划,我们希望使用 Elasticsearch 作为搜索引擎。在这一点上,我们在索引某些字段时遇到了问题,因为它们会在 _all 字段上生成一个“巨大的术语”错误。

我们的设置:

curl -XGET 'http://localhost:9200/my_index?pretty=true'
{
  "my_index" : {
    "aliases" : { },
    "mappings" : {
      "Memberships" : {
        "_all" : {
          "analyzer" : "keylower"
        },
        "properties" : {
          "Amount" : {
            "type" : "float"
          },
          "Members" : {
            "type" : "nested",
            "properties" : {
              "Startdate membership" : {
                "type" : "date",
                "format" : "dateOptionalTime"
              },
              "Enddate membership" : {
                "type" : "date",
                "format" : "dateOptionalTime"
              },
              "Members" : {
                "type" : "string",
                "analyzer" : "keylower"
              }
            }
          },
          "Membership name" : {
            "type" : "string",
            "analyzer" : "keylower"
          },
          "Description" : {
            "type" : "string",
            "analyzer" : "keylower"
          },
          "elementId" : {
            "type" : "integer"
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1441310632366",
        "number_of_shards" : "1",
        "analysis" : {
          "filter" : {
            "my_char_filter" : {
              "type" : "asciifolding",
              "preserve_original" : "true"
            }
          },
          "analyzer" : {
            "keylower" : {
              "filter" : [ "lowercase", "my_char_filter" ],
              "tokenizer" : "keyword"
            }
          }
        },
        "number_of_replicas" : "1",
        "version" : {
          "created" : "1040599"
        },
        "uuid" : "nn16-9cTQ7Gn9NMBlFxHsw"
      }
    },
    "warmers" : { }
  }
}

我们使用 keylower-analyzer,因为我们不希望全名被空格分割。这是因为我们希望能够在 _all 字段以及 'Members' 字段中搜索 'john johnson'。

“成员”字段可以包含多个成员,这就是问题开始的地方。当该字段仅包含几个成员时(如下例所示),没有问题。但是,该字段可能包含数百或数千个成员,这就是我们得到 immens term 错误的时候。

curl 'http://localhost:9200/my_index/_search?pretty=true&q=*:*'
{  
   "took":1,
   "timed_out":false,
   "_shards":{  
      "total":1,
      "successful":1,
      "failed":0
   },
   "hits":{  
      "total":1,
      "max_score":1.0,
      "hits":[  
         {  
            "_index":"my_index",
            "_type":"Memberships",
            "_id":"15",
            "_score":1.0,
            "_source":{  
               "elementId":[  
                  "15"
               ],
               "Membership name":[  
                  "My membership"
               ],
               "Amount":[  
                  "100"
               ],
               "Description":[  
                  "This is the description."
               ],
               "Members":[  
                  {  
                     "Members":"John Johnson",
                     "Startdate membership":"2015-01-09",
                     "Enddate membership":"2015-09-03"
                  },
                  {  
                     "Members":"Pete Peterson",
                     "Startdate membership":"2015-09-09"
                  },
                  {  
                     "Members":"Santa Claus",
                     "Startdate membership":"2015-09-16"
                  }
               ]
            }
         }
      ]
   }
}

注意:上面的例子有效!只有当“成员”字段包含(很多)更多成员时,我们才会收到错误消息。我们得到的错误是:

"error":"IllegalArgumentException[文档包含至少一个 field=\"_all\" 中的巨大术语(其 UTF8 编码比 最大长度 32766),所有这些都被跳过了。请更正 分析仪不产生这样的条款。第一个巨大的前缀 术语是:'[...]...',原始消息:字节最多可以是 32766 in 长度;得到 106807];嵌套:MaxBytesLengthExceededException[字节可以 长度最多为 32766;得到 106807]; " "状态":500

我们只在 _all 字段上收到此错误,而不是在原始成员字段上。使用ignore_above,不再可以在全名的_all 字段中进行搜索。使用标准分析器,如果我搜索“Santa Johnson”,我会找到这个文档,因为 _all-fields 有一个标记“Santa”和“Johnson”。这就是我对这些字段使用 keylower 的原因。

我想要的是一个分析器,它可以对字段进行标记,但不会分解字段本身的值。现在发生的情况是,整个字段“成员”被作为一个令牌提供,包括子字段。 (因此,上面示例中的令牌将是:

  • John Johnson 2015-01-09 2015-09-03 Pete Peterson 2015-09-09 圣诞老人 2015-09-16

是否可以对这些字段进行标记,使每个字段都作为单独的标记提供给 _all,但又不分解字段本身的值?这样令牌将是:

  • 约翰·约翰逊
  • 2015-01-09
  • 2015-09-03
  • 皮特·彼得森
  • 2015-09-09
  • 圣诞老人
  • 2015-09-16

注意:我们使用 Elasticsearch php 库。

【问题讨论】:

  • This answer 应该可以帮到你。
  • 只想补充@Val 所说的内容。除了将 _all 字段视为单个非标记化 blob 并使用非常昂贵的子字符串搜索之外,还有更好的替代方法。例如,您可以在 _all 字段上使用标准分析器,然后搜索短语“John Johnson”。或者您可以使用单个过滤器索引 _all 并使用正常匹配查询搜索 2 或 3 个单词短语。也许,如果您可以用更完整的要求更新您的问题,我们将能够为您提供更好、性能更高的解决方案。
  • @imotov 我已经编辑了这个问题,包括我们不使用默认分析器和 ignore_above 的原因。
  • @wjhulzebosch 你没有回答为什么不能使用标准分析器和phrase search
  • @imotov 如果我对匹配/短语查询的理解是正确的,我做到了:我不想在搜索“Santa Johnson”时找到这个文档,但是当我使用默认分析器和然后执行这个搜索,这个文档会弹出,因为它有一个标记'Santa'和'Johnson'。也许我现在错了?

标签: php elasticsearch


【解决方案1】:

有一个更好的方法来做到这一点。短语搜索是否可以跨越多个字段值由position_offset_gap 决定(在2.0 中将重命名为position_increment_gap)。此参数基本上指定在一个字段的最后一个标记和以下字段的第一个标记之间应该“插入”多少个单词/位置。默认情况下,在 2.0 之前的弹性搜索中,position_increment_gap 的值为 0。这就是导致您描述的问题的原因。

通过组合copy_to 功能并指定position_increment_gap,您可以创建一个不会出现此问题的替代my_all 字段。通过在index.query.default_field 设置中设置这个新字段,您可以告诉elasticsearch 在未指定字段时默认使用此字段而不是_all 字段。

curl -XDELETE "localhost:9200/test-idx?pretty"
curl -XPUT "localhost:9200/test-idx?pretty" -d '{
    "settings" :{
        "index": {
            "number_of_shards": 1,
            "number_of_replicas": 0,
            "query.default_field": "my_all"
        }
    },
    "mappings": {
        "doc": {
            "_all" : {
                "enabled" : false
            },
            "properties": {
                "Members" : {
                  "type" : "nested",
                  "properties" : {
                    "Startdate membership" : {
                      "type" : "date",
                      "format" : "dateOptionalTime",
                      "copy_to": "my_all"
                    },
                    "Enddate membership" : {
                      "type" : "date",
                      "format" : "dateOptionalTime",
                      "copy_to": "my_all"
                    },
                    "Members" : {
                      "type" : "string",
                      "analyzer" : "standard",
                      "copy_to": "my_all"
                    }
                  }
                },
                "my_all" : {
                    "type": "string",
                    "position_offset_gap": 256
                }
            }
        }
    }
}'
curl -XPUT "localhost:9200/test-idx/doc/1?pretty" -d '{
    "Members": [{
        "Members": "John Johnson",
        "Startdate membership": "2015-01-09",
        "Enddate membership": "2015-09-03"
    }, {
        "Members": "Pete Peterson",
        "Startdate membership": "2015-09-09"
    }, {
        "Members": "Santa Claus",
        "Startdate membership": "2015-09-16"
    }]
}'
curl -XPOST "localhost:9200/test-idx/_refresh?pretty"
echo
echo "Should return one hit"
curl "localhost:9200/test-idx/doc/_search?pretty=true" -d '{
    "query": {
        "match_phrase" : {
            "my_all" : "John Johnson"
        }
    }
}'
echo
echo "Should return one hit"
curl "localhost:9200/test-idx/doc/_search?pretty=true" -d '{
    "query": {
        "query_string" : {
            "query" : "\"John Johnson\""
        }
    }
}'
echo
echo "Should return no hits"
curl "localhost:9200/test-idx/doc/_search?pretty=true" -d '{
    "query": {
        "match_phrase" : {
            "my_all" : "Johnson 2015-01-09"
        }
    }
}'
echo
echo "Should return no hits"
curl "localhost:9200/test-idx/doc/_search?pretty=true" -d '{
    "query": {
        "query_string" : {
            "query" : "\"Johnson 2015-01-09\""
        }
    }
}'
echo
echo "Should return no hits"
curl "localhost:9200/test-idx/doc/_search?pretty=true" -d '{
    "query": {
        "match_phrase" : {
            "my_all" : "Johnson Pete"
        }
    }
}'

【讨论】:

  • 嗨,我刚刚对此进行了测试,这似乎可以正常工作。我遇到的唯一问题是我不能将 match_phrase 与通配符一起使用(例如,当我想搜索“Santa Cl *”时。我知道这实际上是一个单独的问题,但对我们来说重要的是这是可能的。我还没有找到 match_phrase 与通配符的组合。这可能还是我应该为此使用 nGrams?
  • 我想你在这里寻找的是match_phrase_prefix。大量的通配符扩展有一些警告,但这绝对是在 cmets 中讨论的太有趣了。
猜你喜欢
  • 1970-01-01
  • 2014-11-18
  • 2019-01-05
  • 1970-01-01
  • 1970-01-01
  • 2020-06-12
  • 2021-07-25
  • 1970-01-01
  • 2019-02-16
相关资源
最近更新 更多