字段未按弹性搜索的字母顺序排序

时间:2015-10-28 04:05:16

标签: elasticsearch elasticsearch-mapping

我有一些带有名字字段的文件。我使用名称字段的分析版本进行搜索,使用not_analyzed进行排序。排序发生在一个级别,即名称首先按字母顺序排序。但是在字母表列表中,名称按字典顺序排序,而不是按字母顺序排序。 这是我使用的映射:

{
  "mappings": {
    "seing": {
      "properties": {
        "name": {
          "type": "string",
          "fields": {
            "raw": {
              "type": "string",
              "index": "not_analyzed"
            }
          }
        }
      }
    }
  }

任何人都可以为此提供解决方案吗?

3 个答案:

答案 0 :(得分:13)

深入研究Elasticsearch文档,我偶然发现了这个:

不区分大小写的排序

  

想象一下,我们有三个名称字段包含的用户文档   分别是Boffey,BROWN和bailey。首先我们将申请   在字符串排序和使用a的多字段中描述的技术   not_analyzed字段用于排序:

PUT /my_index
{
  "mappings": {
    "user": {
      "properties": {
        "name": {                    //1
          "type": "string",
          "fields": {
            "raw": {                 //2
              "type":  "string",
              "index": "not_analyzed"
            }
          }
        }
      }
    }
  }
}
  1. analyzed name字段用于搜索。
  2. not_analyzed name.raw字段用于排序。
  3.   

    上述搜索请求将按此顺序返回文档:   布朗,波菲,贝利。这被称为词典顺序   反对按字母顺序排列。基本上,用于的字节   表示大写字母的值低于用于的字节   表示小写字母,因此名称用   首先是最低字节。

         

    这可能对计算机有意义,但没有多大意义   人们会合理地期望对这些名称进行排序   按字母顺序排列,无论如何。要实现这一点,我们需要索引   每个名称的字节排序方式与排序相对应   我们想要的订单。

         

    换句话说,我们需要一个发射单个小写的分析器   令牌:

    遵循这个逻辑,您需要使用自定义关键字分析器小写它来代替存储原始文档:

    PUT /my_index
    {
      "settings" : {
        "analysis" : {
          "analyzer" : {
            "case_insensitive_sort" : {
              "tokenizer" : "keyword",
              "filter" : ["lowercase"]
            }
          }
        }
      },
      "mappings" : {
        "seing" : {
          "properties" : {
            "name" : {
              "type" : "string",
              "fields" : {
                "raw" : {
                  "type" : "string",
                  "analyzer" : "case_insensitive_sort"
                }
              }
            }
          }
        }
      }
    }
    

    现在按name.raw排序应按按字母顺序顺序排序,而不是词典

    使用Marvel在本地计算机上进行快速测试:

    索引结构:

    PUT /my_index
    {
      "settings": {
        "analysis": {
          "analyzer": {
            "case_insensitive_sort": {
              "tokenizer": "keyword",
              "filter": [
                "lowercase"
              ]
            }
          }
        }
      },
      "mappings": {
        "user": {
          "properties": {
            "name": {
              "type": "string",
              "fields": {
                "raw": {
                  "type": "string",
                  "index": "not_analyzed"
                },
                "keyword": {
                  "type": "string",
                  "analyzer": "case_insensitive_sort"
                }
              }
            }
          }
        }
      }
    }
    

    测试数据:

    PUT /my_index/user/1
    {
      "name": "Tim"
    }
    
    PUT /my_index/user/2
    {
      "name": "TOM"
    }
    

    使用原始字段查询:

    POST /my_index/user/_search
    {
      "sort": "name.raw"
    }
    

    结果:

    {
      "_index" : "my_index",
      "_type" : "user",
      "_id" : "2",
      "_score" : null,
      "_source" : {
        "name" : "TOM"
      },
      "sort" : [
        "TOM"
      ]
    },
    {
      "_index" : "my_index",
      "_type" : "user",
      "_id" : "1",
      "_score" : null,
      "_source" : {
        "name" : "Tim"
      },
      "sort" : [
        "Tim"
      ]
    }
    

    使用小写字符串查询:

    POST /my_index/user/_search
    {
      "sort": "name.keyword"
    }
    

    结果:

    {
      "_index" : "my_index",
      "_type" : "user",
      "_id" : "1",
      "_score" : null,
      "_source" : {
        "name" : "Tim"
      },
      "sort" : [
        "tim"
      ]
    },
    {
      "_index" : "my_index",
      "_type" : "user",
      "_id" : "2",
      "_score" : null,
      "_source" : {
        "name" : "TOM"
      },
      "sort" : [
        "tom"
      ]
    }
    

    我怀疑你的第二个结果是正确的。

答案 1 :(得分:1)

自Elastic 5.2起,您可以使用normaliser设置不区分大小写的排序。

normalizer字段的keyword属性类似于 analyzer,但可以保证分析链 产生一个令牌。

在对关键字建立索引之前以及在normalizer处应用 通过查询解析器搜索keyword字段时的搜索时间 match查询。

PUT index
{
  "settings": {
    "analysis": {
      "normalizer": {
        "my_normalizer": {
          "type": "custom",
          "char_filter": [],
          "filter": ["lowercase", "asciifolding"]
        }
      }
    }
  },
  "mappings": {
    "type": {
      "properties": {
        "foo": {
          "type": "keyword",
          "normalizer": "my_normalizer"
        }
      }
    }
  }
}

PUT index/type/1
{
  "foo": "BÀR"
}

PUT index/type/2
{
  "foo": "bar"
}

PUT index/type/3
{
  "foo": "baz"
}

POST index/_refresh

GET index/_search
{
  "query": {
    "match": {
      "foo": "BAR"
    }
  }
}

由于BÀR在以下位置转换为bar,因此上述查询匹配文档1和2: 索引和查询时间。

{
  "took": $body.took,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 0.2876821,
    "hits": [
      {
        "_index": "index",
        "_type": "type",
        "_id": "2",
        "_score": 0.2876821,
        "_source": {
          "foo": "bar"
        }
      },
      {
        "_index": "index",
        "_type": "type",
        "_id": "1",
        "_score": 0.2876821,
        "_source": {
          "foo": "BÀR"
        }
      }
    ]
  }
}

此外,在索引之前先转换关键字这一事实也意味着 聚合返回归一化值:

GET index/_search
{
  "size": 0,
  "aggs": {
    "foo_terms": {
      "terms": {
        "field": "foo"
      }
    }
  }
}

返回

{
  "took": 43,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 3,
    "max_score": 0.0,
    "hits": []
  },
  "aggregations": {
    "foo_terms": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "bar",
          "doc_count": 2
        },
        {
          "key": "baz",
          "doc_count": 1
        }
      ]
    }
  }
}

来源:Normaliser

答案 2 :(得分:0)

只是一些基于@PiwEL 回答的附加组件。

如果您使用的是 nodejs version 的弹性搜索,如果您的索引已经存在,则您不能真正更新设置:

await esClient.indices.putSettings({
      index,
      body: {
        analysis: {
          normalizer: {
            my_normalizer: {
              type: "custom",
              char_filter: [],
              filter: ["lowercase", "asciifolding"],
            },
          },
        },
      },
    });

这个 API 总是抛出异常。所以我最终在创建索引时创建了这个设置,因此必须删除并重新创建索引。更多细节可追溯here

所以当使用 NodeJs 客户端时,以下对我有用。

await esClient.indices.create({
  index,
  body: {
    settings: {
      analysis: {
        normalizer: {
          my_normalizer: {
            type: "custom",
            char_filter: [],
            filter: ["lowercase", "asciifolding"],
          },
        },
      },
    },
    mappings: {
      properties: {
        id: {
          type: "keyword",
        },
        name: {
          type: "text",
          fields: {
            keyword: {
              type: "keyword",
              normalizer: "my_normalizer",
            },
          },
        },
        seedless: {
          type: "boolean",
        },
        origin: { type: "text" },
      },
    },
  },
});