详细教程:elasticsearch基于script统计索引中某字段(keyword或者text类型)的字符串长度

目录

1.需求场景

2.实现方案

2.1 keyword类型

2.2 text类型

设置fielddata=true

reindex把text文本改keyword

3.参考资料


1.需求场景

统计es索引中某字段的字符串长度有多种使用场景,例如:

1.想统计出索引文本内容分别在300字内、500字内、1000字内的文章数量;

2.将查询结果按某字段的字符串长度排序;

3.想实验过滤短文本后的查询效果 ......

2.实现方案

elasticsearch 5.0及之后的版本中存储字符串的类型有两种,keyword和text。

text属性的字段在es中存储时,会被自动分词存储,因此text属性的字段支持分词,但不支持 过滤、排序和聚合等操作。keyword属性的字段更方便统计,但通常情况下,对于存储文本内 容的content我们在设置索引mapping的时候都会将其设为text,这一设置方便了我们进行关键 词的分词全文检索,但当想要对text类型字段进行聚合等相关统计操作时带来不便。

下面针对两种类型分别介绍基于script统计字符串长度的步骤

2.1 keyword类型

#查找标签长度小于5
GET wendongmi_read/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "script": {
            "script": {
                    "source": """
                    if (doc['_tags'].size() !=0) {
                    doc['_tags'].value.length() < 5
                    }
                    """,
                    "lang": "painless"
                }
          }
        }
      ]
    }
  },
  "track_total_hits": true,
  "size": 10
}

因为keyword类型本身便可进行聚合、过滤、排序等,可以直接使用 doc['field_name'].valu e.length() 的语法获取该属性值的长度进行相关过滤统计。

2.2 text类型

当尝试直接对text类型字段使用如keyword类型一样的语法时,会出现如下报错:

Text fields are not optimised for operations that require per-document field data like aggregations and sorting, so these operations are disabled by default. Please use a keyword field instead. Alternatively, set fielddata=true on [Art_Content] in order to load field data by uninverting the inverted index. Note that this can use significant memory.

针对text类型,其实es官方报错的提示语已经给如何对text类型做统计指明了两个方向。

进行fielddata=true设 置或者把该字段变成keyword类型 

设置fielddata=true
POST test_read/_mapping
{
  "properties":{
    "content": {
      "type" : "text",
      "fielddata": false
    }
  }
}

在es中,text类型的字段使用一种叫做fielddata的查询时内存数据结构。当字段被排序,聚合 或者通过脚本访问时这种数据结构会被创建。它是通过从磁盘读取每个段的整个反向索引来构 建的,然后存存储在java的堆内存中。

fileddata默认是不开启的。fileddata可能会消耗大量的堆空间,尤其是在加载高基数文本字段 时。一旦fielddata已加载到堆中,它将在该段的生命周期内保留。此外,加载fielddata是一个 昂贵的过程,可能会导致用户遇到延迟命中。这就是默认情况下禁用fielddata的原因。不推荐在生产环境使用

设置后,即可使用script统计成功(ps:速度很慢)

GET test_read/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "script": {
            "script": {
                    "source": """
                    if (doc['content'].size() !=0) {
                    doc['content'].value.length() < 5
                    }
                    """,
                    "lang": "painless"
                }
          }
        }
      ]
    }
  },
  "track_total_hits": true,
  "size": 10
}
reindex把text文本改keyword

如果是暂时统计一下获取某个数据的话(比如资讯文章里文本300字内的文章数)可以新设置一个专用于统计的索引,将要统计的字段设为keyword,将需要统计的数据reindex到该索引

这里需要注意的是,ES5.X版本以后,keyword支持的最大长度为32766个UTF-8字符。如果待统计的本文较长,直接reindex会因为长度超过keyword支持的最大长度而报错,这时需要设置ignore_above,设置ignore_above后,超过给定长度后的数据将不被索引,无法通过term精确匹配检索返回结果。

PUT test_stat
{
  "aliases": {
    "test_stat_read": {},
    "test_stat_write": {}
  },
  "mappings": {
    "properties": {
      "type" : {     
          "type" : "keyword"
        },
      "content" : {   
        "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 3000
            }
          }
        },
      "title" : {                                                    
          "type" : "keyword"
        }
    }
  },
  "settings": {
    "index": {
      "refresh_interval": "1s",
      "number_of_shards": "1",
      "number_of_replicas": "0"
    }
  }
}

设置后,即可通过以下脚本进行统计

#查找content长度大于1000的
GET test_stat/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "script": {
            "script": {
                    "source": """
                    if (doc['content.keyword'].size() !=0) {
                    doc['content.keyword'].value.length() > 1000
                    }
                    """,
                    "lang": "painless"
                }
          }
        }
      ]
    }
  },
  "track_total_hits": true,
  "size": 0
}

语法总结

  • type为text时,我们可以通过doc['field_name'].length或者doc['field_name'].size()获取该属性对应数组的长度;

  • type为keyword时,则使用doc['field_name'].value.length()获取属性值的长度,但是需要注意,如果doc['field_name'].value的值存在为null的情况,因此需要使用doc['field_name'].size()优先判空。

3.参考资料

[Text type family | Elasticsearch Guide [8.8] | Elastic]

Text type family | Elasticsearch Guide [8.8] | Elastic

[Elasticsearch:如何基于Script实现按照text属性值的字符串长度排序

https://www.cnblogs.com/mrzihan/p/15729353.html

[Lucene expressions language | Elasticsearch Guide [8.9] | Elastic]

Lucene expressions language | Elasticsearch Guide [8.9] | Elastic

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值