在采用es的bucket agg的时候,我们会经常操作string类型的字段,一个string字段设置为analyzed和not_analyzed对聚合有什么样的影响呢?
结论是影响非常大。下面是一个例子,先bulk进一批数据
POST /agg_analysis/data/_bulk
{ "index": {}}
{ "state" : "New York" }
{ "index": {}}
{ "state" : "New Jersey" }
{ "index": {}}
{ "state" : "New Mexico" }
{ "index": {}}
{ "state" : "New York" }
{ "index": {}}
{ "state" : "New York" }
然后通过term聚合去获取结果
GET /agg_analysis/data/_search?search_type=count
{
"aggs" : {
"states" : {
"terms" : {
"field" : "state"
}
}
}
}
结果是:
{
...
"aggregations": {
"states": {
"buckets": [
{
"key": "new",
"doc_count": 5
},
{
"key": "york",
"doc_count": 3
},
{
"key": "jersey",
"doc_count": 1
},
{
"key": "mexico",
"doc_count": 1
}
]
}
}
}
这可不是我们想要的,聚合竟然是根据每个单词聚合的,为啥会这样?原因很简单:聚合的数据是从倒排索引中建立的,而倒排索引的数据是analyzed的。
我们需要做的是重新设置mapping
DELETE /agg_analysis/
PUT /agg_analysis
{
"mappings": {
"data": {
"properties": {
"state" : {
"type": "string",
"fields": {
"raw" : {
"type": "string",
"index": "not_analyzed"
}
}
}
}
}
}
}
POST /agg_analysis/data/_bulk
{ "index": {}}
{ "state" : "New York" }
{ "index": {}}
{ "state" : "New Jersey" }
{ "index": {}}
{ "state" : "New Mexico" }
{ "index": {}}
{ "state" : "New York" }
{ "index": {}}
{ "state" : "New York" }
GET /agg_analysis/data/_search?search_type=count
{
"aggs" : {
"states" : {
"terms" : {
"field" : "state.raw"
}
}
}
}
This time we explicitly map the state field and include a not_analyzed sub-field.
The aggregation is run on state.raw instead of state.
Now when we run our aggregation,we get results that make sense:
{
...
"aggregations": {
"states": {
"buckets": [
{
"key": "New York",
{
"key": "New Jersey",
{
"key": "New Mexico",
"doc_count": 1
}
]
}
}
}
在日常开发中这种问题总会出现,你要记住这个issue,通常,很少会有业务使用分析字段做聚合的,如果真的出现,你的做法就是加个字段然后同样的值然后分别使用即可。
high-cardinality memory implications 极其重要的内存潜在问题
这里还有个重要的理由避免聚合analyzed字段,因为把这些字段加入fielddata之后会占用大量的内存,这分析过程会生成大量的token,每个token都是独立的,大量的数据会占用大量的内存。
例如new york被analyzed会有
ne
ew
w
y
yo
or
rk
你可以想象这是多么可怕,特别是在分析段落字段的时候(例如商品描述),很容易造成内存溢出。
所以,在通过这些字段聚合之前,核对这个字段是否是analyzed的,不管是不是analzyzed的,越长的string字段绝对内存占用越大,所以得注意一下。