elasticsearch聚合:script agg 和 filter agg 的實(shí)際使用

背景介紹

需要對索引中的做聚合寝蹈,但是聚合的條件會比較復(fù)雜讼呢,并非從單一字段進(jìn)行聚合帽馋。參考如下數(shù)據(jù)結(jié)構(gòu)

{
        //空格分詞字段
    "feild_a": "1  2 3 4 5",
        //keyword字段
    "feild_b": "2"
}

要做的事:我們有 1 2 3 …… 等id,需要統(tǒng)計(jì)索引里面谬哀,a和b字段分別含有1 2 3對應(yīng)的數(shù)量刺覆。文字難以表述的需求我們轉(zhuǎn)化成數(shù)據(jù)結(jié)構(gòu)來看

{
    "data": [{
        "id": "1",
        "count": 1
    }, {
        "id": "2",
        "count": 2
    }, {
        "id": "3",
        "count": 1
    }]
}

如果只有上面那一條數(shù)據(jù)的話,那么應(yīng)該得出下面這個(gè)統(tǒng)計(jì)結(jié)果史煎。

問題分析

從表面剖析這個(gè)需求的話谦屑,似乎是個(gè)多字段的聚合。相對于單字段聚合來說問題還是比較棘手的篇梭。并不能簡單的通過單字段的聚合來解決問題氢橙,我們先從最簡單的情況開始處理問題。以統(tǒng)計(jì)1 2 3這3個(gè)id來舉例子恬偷。

1.單字段情況下聚合

假設(shè)只需要對一個(gè)字段聚合悍手,比如b字段,b字段是keyword類型袍患,需要考慮的情況最為簡單坦康,當(dāng)要對b字段聚合時(shí)語句很好寫,如下即可

{
    "from": 0,
    "size": 0,
    "query": {
        "bool": {
            "must": [{
                "bool": {
                    "should": [{
                        "terms": {
                            "field_a": ["1", "2", "3"],
                            "boost": 1.0
                        }
                    }, {
                        "terms": {
                            "field_b": ["1", "2", "3"],
                            "boost": 1.0
                        }
                    }],
                    "adjust_pure_negative": true,
                    "minimum_should_match": "1",
                    "boost": 1.0
                }
            }],
            "adjust_pure_negative": true,
            "boost": 1.0
        }
    },
    "aggregations": {
        "my_agg": {
            "terms": {
                "field": "field_b"
            }
        }
    }
}

這是完整的query诡延,后面的查詢會省略掉query部分滞欠。query部分的用處也很明顯:只把需要做聚合的部分過濾出來做聚合,我們需要統(tǒng)計(jì)的數(shù)據(jù)就在這部分中肆良,而不是整個(gè)索引庫筛璧。這樣有兩個(gè)好處:
1.提高效率逸绎,減少需要聚合的數(shù)據(jù)的數(shù)量
2.剔除需要考慮的意外情況,降低語句的復(fù)雜度
而聚合部分就非常簡單了夭谤,僅僅對field_b聚合即可棺牧,但是很遺憾,離我們最終目標(biāo)很遠(yuǎn)沮翔,這樣只能統(tǒng)計(jì)出b字段的數(shù)據(jù)分布情況陨帆。

2.多字段情況的聚合

相對于上面的那種曲秉,接下來把另外一個(gè)字段也考慮進(jìn)來看看采蚀。所以我們寫下了這樣的請求語句:

  "aggregations": {
    "my_agg1": {
      "terms": {
        "field": "tag_brand_id"
      }
    },
    "my_agg2": {
      "terms": {
        "field": "brand_cid_array"
      }
    }
  }

勉強(qiáng)的可以看到確實(shí)也是“統(tǒng)計(jì)了兩個(gè)字段的情況”,但是是分開的承二,意味著要自己去解析返回結(jié)果并做計(jì)算來得到最終的返回結(jié)果榆鼠。這確實(shí)是很令人惡心的事,那還有沒有其他辦法呢亥鸠。但是觀察語句的結(jié)構(gòu)發(fā)現(xiàn)妆够,似乎并沒有過多可以更改的余地,所以需要尋求其他靈活的解決辦法负蚊。

3.script agg的聚合

簡單的單聚合無法表達(dá)出多字段聚合的需求神妹,在谷歌過后我尋找到了這樣一種解決方案:使用script,即腳本來描述我的需求家妆。下面這段agg就是為了表達(dá)我想要根據(jù)我的需求靈活處理的一個(gè)方式:

  "aggregations": {
    "my_agg1": {
      "terms": {
        "script": " if (doc['field_a'].values.contains('1') || doc['field_b'].values.contains('1')){1};if (doc['field_a'].values.contains('2') || doc['field_b'].values.contains('2')){2};
if (doc['field_a'].values.contains('3') || doc['field_b'].values.contains('3')){3};"
      }
    }
  }

這一段腳本的作用很明顯鸵荠,就是告訴es:當(dāng)a字段或者b字段包括1的時(shí)候,扔到桶1伤极;當(dāng)a字段或者b字段包括2的時(shí)候蛹找,扔到桶2;……以此類推哨坪∮辜玻看上去確實(shí)似乎完全解決了開頭提出來的問題,驗(yàn)證后效率還能接受当编,不是特別慢届慈。但是正當(dāng)我沾沾自喜以為解決了問題的時(shí)候,隨手驗(yàn)證了另外一個(gè)case忿偷,就直接冷水潑頭了:
a字段和b字段是可能包含同一個(gè)id比如2金顿,但是對于統(tǒng)計(jì)結(jié)果來說要求算作一條。
用上面這個(gè)腳本并無法體現(xiàn)出這個(gè)區(qū)別牵舱,而且還會有一個(gè)問題:
請求123和請求321時(shí)會返回不同統(tǒng)計(jì)結(jié)果
因?yàn)閕felse語句的關(guān)系串绩,和||的性質(zhì),在滿足條件1后便會扔到桶1芜壁,而無法在去后續(xù)條件中判斷礁凡。這個(gè)腳本有很明顯的bug存在高氮。但是painless畢竟是腳本,可以使用的API和關(guān)鍵字都非常有限顷牌,寫的復(fù)雜了還會很嚴(yán)重影響效率剪芍,無奈這個(gè)方案也只能pass,即使它看上去差點(diǎn)解決了我的問題窟蓝。

4.filter agg的聚合

在重新看了官方文檔后罪裹,我發(fā)現(xiàn)了agg中的一個(gè)用法,filter agg运挫。
filter agg的用法其實(shí)很簡單状共,但是全意外的和我的需求很契合。之前忽視掉這個(gè)用法的主要原因是看到的示例都是對單字段做聚合谁帕。那如何同時(shí)聚合多個(gè)字段呢峡继?從API入手驗(yàn)證是否可以使用比較靈活的寫法

        public KeyedFilter(String key, QueryBuilder filter) {
            if (key == null) {
                throw new IllegalArgumentException("[key] must not be null");
            }
            if (filter == null) {
                throw new IllegalArgumentException("[filter] must not be null");
            }
            this.key = key;
            this.filter = filter;
        }

這是es提供的javaapi中filter agg的構(gòu)造函數(shù),key就是過濾名稱匈挖,filter就是過濾條件碾牌。而且很友好的是,filter類型為QueryBuilder儡循,也就是說舶吗,可以做成比較復(fù)雜的過濾方式。

    "aggregations": {
        "batch_count": {
            "filters": {
                "filters": {
                    "1": {
                        "bool": {
                            "should": [{
                                "term": {
                                    "field_a": {
                                        "value": "1",
                                        "boost": 1.0
                                    }
                                }
                            }, {
                                "term": {
                                    "field_b": {
                                        "value": "1",
                                        "boost": 1.0
                                    }
                                }
                            }],
                            "adjust_pure_negative": true,
                            "boost": 1.0
                        }
                    },
                    "2": {
                        "bool": {
                            "should": [{
                                "term": {
                                    "field_a": {
                                        "value": "2",
                                        "boost": 1.0
                                    }
                                }
                            }, {
                                "term": {
                                    "field_b": {
                                        "value": "2",
                                        "boost": 1.0
                                    }
                                }
                            }],
                            "adjust_pure_negative": true,
                            "boost": 1.0
                        }
                    },
                    "3": {
                        "bool": {
                            "should": [{
                                "term": {
                                    "field_a": {
                                        "value": "3",
                                        "boost": 1.0
                                    }
                                }
                            }, {
                                "term": {
                                    "field_b": {
                                        "value": "3",
                                        "boost": 1.0
                                    }
                                }
                            }],
                            "adjust_pure_negative": true,
                            "boost": 1.0
                        }
                    }
                },
                "other_bucket": false,
                "other_bucket_key": "-1"
            }
        }
    }

這就是最后成型的agg塊

問題總結(jié)

agg模塊的開發(fā)是比較麻煩的择膝,首先性能問題比較困擾誓琼,其次語句編寫遠(yuǎn)沒有query模塊的靈活。這次順利解決需求调榄,記錄踊赠。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市每庆,隨后出現(xiàn)的幾起案子筐带,更是在濱河造成了極大的恐慌,老刑警劉巖缤灵,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伦籍,死亡現(xiàn)場離奇詭異,居然都是意外死亡腮出,警方通過查閱死者的電腦和手機(jī)帖鸦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胚嘲,“玉大人作儿,你說我怎么就攤上這事〔雠” “怎么了攻锰?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵晾嘶,是天一觀的道長。 經(jīng)常有香客問我娶吞,道長垒迂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任妒蛇,我火速辦了婚禮机断,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绣夺。我一直安慰自己吏奸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布乐导。 她就那樣靜靜地躺著苦丁,像睡著了一般浸颓。 火紅的嫁衣襯著肌膚如雪物臂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天产上,我揣著相機(jī)與錄音棵磷,去河邊找鬼。 笑死晋涣,一個(gè)胖子當(dāng)著我的面吹牛仪媒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谢鹊,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼算吩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了佃扼?” 一聲冷哼從身側(cè)響起偎巢,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎兼耀,沒想到半個(gè)月后压昼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瘤运,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年窍霞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拯坟。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡但金,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出郁季,到底是詐尸還是另有隱情冷溃,我是刑警寧澤掠哥,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站秃诵,受9級特大地震影響续搀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜菠净,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一禁舷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧毅往,春花似錦牵咙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至侯嘀,卻和暖如春另凌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背戒幔。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工吠谢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诗茎。 一個(gè)月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓工坊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親敢订。 傳聞我的和親對象是個(gè)殘疾皇子王污,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容