【问题标题】:How to search by non-tokenized field length in ElasticSearch如何在 ElasticSearch 中按非标记字段长度进行搜索
【发布时间】:2021-06-03 08:14:55
【问题描述】:

假设我创建了一个索引 people,它将获取具有两个属性的条目:namefriends

PUT /people
{
  "mappings": {
    "properties": {
      "friends": { 
        "type": "text",
        "fields": {
          "keyword": { 
            "type": "keyword"
          }
        }
      }
    }
  }
}

我放了两个条目,每个条目都有两个朋友。

POST /people/_doc
{
  "name": "Jack",
  "friends": [
    "Jill", "John"
  ]
}


POST /people/_doc
{
  "name": "Max",
  "friends": [
    "John", "John"  # Max will have two friends, but both named John
  ]
}

现在我想搜索有多个朋友的人

GET /people/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "script": {
            "script": {
              "source": "doc['friends.keyword'].length > 1"
            }
          }
        }
      ]
    }
  }
}

这只会返回 Jack 并忽略 Max。我假设这是因为我们实际上是在遍历倒排索引,而 John 和 John 只创建了一个标记 - 'john',所以这里标记的长度实际上是 1。

由于我的索引比较小,性能不是关键,所以我想实际遍历源而不是倒排索引

GET /people/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "script": {
            "script": {
              "source": "ctx._source.friends.length > 1"
            }
          }
        }
      ]
    }
  }
}

但根据https://github.com/elastic/elasticsearch/issues/20068的说法,只有更新时才支持源,搜索时不支持,所以我不能。

一个明显的解决方案似乎是获取字段的长度并将其存储到索引中。 friends_count: 2 之类的东西,然后根据它进行过滤。但这需要重新索引,而且这似乎是应该以某种明显的方式解决的问题。

非常感谢。

【问题讨论】:

    标签: elasticsearch elastic-stack


    【解决方案1】:

    ES 7.11 中有一个新特性作为运行时字段运行时字段是在查询时评估的字段。运行时字段使您能够:

    1. 在现有文档中添加字段而不重新索引您的数据
    2. 在不了解数据结构的情况下开始处理数据
    3. 在查询时覆盖从索引字段返回的值
    4. 在不修改基础架构的情况下为特定用途定义字段

    你可以找到更多关于运行时字段的信息here,但是你可以如何使用运行时字段,你可以这样做:

    索引时间:

    PUT my-index/
    {
      "mappings": {
        "runtime": {
          "friends_count": {
            "type": "keyword",
            "script": {
              "source": "doc['@friends'].size()"
            }
          }
        },
        "properties": {
          "@timestamp": {"type": "date"}
        }
      }
    }
    

    您还可以在搜索时使用运行时字段以获取更多信息检查here

    搜索时间

    GET my-index/_search
    {
      "runtime_mappings": {
        "friends_count": {
          "type": "keyword",
          "script": {
            "source": "ctx._source.friends.size()"
          }
        }
      }
    }
    
    

    更新:

    POST mytest/_update_by_query
    {
        "query": {
            "match_all": {}
        }, 
        "script": {
           "source": "ctx._source.arrayLength = ctx._source.friends.size()"
        }
    }
    

    您可以使用上面的查询更新所有文档并调整您的查询。

    【讨论】:

    • 非常感谢您的回答。虽然我认为 script_fields 或 runtime_mappings 可能是解决类似问题的方法,但我没有设法利用它来解决问题。我认为这是不可能的,因为源可能是在查询完成后创建的,所以我认为它不可能根据非标记字段长度过滤查询(除非在应用程序级别完成)。但也许我错过了什么。
    • 我更新了生成新字段的答案并更新了您的所有文档以添加它。
    • 是的,这也是一种方法。非常感谢!
    【解决方案2】:

    对于想知道同一问题的每个人,我认为@Kaveh 的答案是最有可能的方法,但我没有设法让它在我的情况下工作。在我看来,源是在执行查询后创建的,因此您无法访问源以过滤查询。

    这让您有两个选择:

    • 在应用程序级别过滤结果(丑陋而缓慢的解决方案)
    • 实际上将文件长度保存在单独的字段中。例如 friends_count

    可能还有另一个我不知道的选项(?)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-12
      • 1970-01-01
      • 2015-04-13
      • 1970-01-01
      • 1970-01-01
      • 2011-09-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多