ESFluteによるElasticsearchでのO/Rマッパーを用いた開発
DBFluteフェス2016 1
DBFluteフェス 2016
■ 名前: 菅谷信介■ オープンソース活動:
➔ Fess, DBFlute関連, Apache Portals, Codehaus, Seasar2,...
■ Blog: http://www.chazine.com/■ Twitter: https://twitter.com/shinsuke_sugaya/
自己紹介
2
DBFluteフェス 2016
■ ESFluteとは■ Elasticsearchについて■ ESFluteの使い方
アジェンダ
3
DBFluteフェス 2016
背景
4
DBFluteフェス 2016
■ 全文検索サーバFessでDBFluteを用いて開発➔検索データはSolrで管理➔設定データはデータベースで管理
■ SolrからElasticsearchに移行➔この機会にDBをやめることを決断➔DBがあると構築の手間が増える
■ 大量のBhvやCBを利用したコードが存在➔工数が…
ESFlute誕生の背景
5
DBFluteフェス 2016
BhvやCBでElasticsearchにアクセスできればよいのでは!
6
DBFluteフェス 2016
ESFluteとは
7
DBFluteフェス 2016
■ DBFluteのElasticsearch版➔ CBやBhvが使えます
■ インデックス設定情報からソースの自動生成■ Elasticsearch 1.7と2.xに対応
➔ 5.x系対応は近日対応予定➔ バージョンごとにブランチ管理
■ DBFlute本体にバンドル➔ 将来的には単独の配布にしたい
■ Exampleプロジェクトhttps://github.com/lastaflute/lastaflute-example-waterfrontテストコードもあります!
ESFluteとは
8
DBFluteフェス 2016
Elasticsearchとは
9
DBFluteフェス 2016
■ OSSの分散リアルタイム検索&分析エンジン■ 特徴
➔ドキュメント指向な検索エンジン(Apache Lucene)➔RESTfulなAPI➔スキーマフリー➔分散システム (高可用性、スケールアウト)➔プラグインによる拡張
Elasticsearchとは
10
DBFluteフェス 2016
(物理的な?)構成要素
ESFlute的にはあまり気にしないで良いかも…
11
DBFluteフェス 2016
分散環境的な話…
■ 基本的な構成要素➔ クラスタ➔ ノード➔ インデックス➔ シャード
■ クラスタは複数のノードから構成される■ クラスタは複数のインデックスを保持できる■ インデックスは複数のシャードから構成される■ シャードは1つのプライマリと複数のレプリカで構成
される■ ノードは複数のシャードを保持できる
構成要素
12
DBFluteフェス 2016
構成要素
13
ノード1
ノード3
ノード2
ノード4
インデックス1シャード1
インデックス1シャード2
インデックス2シャード2
インデックス1シャード2
インデックス2シャード1
インデックス1シャード3
インデックス1シャード1
インデックス2シャード1
インデックス1シャード4
インデックス1シャード3
インデックス2シャード2
インデックス1シャード4
クラスタ
DBFluteフェス 2016
(論理的な?)構成要素
ESFlute的には自動生成上、関係ある
14
DBFluteフェス 2016
■ インデックス単位にドキュメントが保存される■ ドキュメントはType属性を持つ➔ドキュメントはタイプに含まれる
■ 検索はインデックスとタイプを指定する
データ観点での構成要素
15
Type
Index
Cluster
Type
Type
Index
Type
DBFluteフェス 2016
■ ESFluteはDBFluteのようなO/Rマッパー■ インデックスを1つのデータベースとして考える■ タイプをテーブルとして考える
ESFluteでの構成要素の対応
16
Type
Index
Cluster
Type
Type
Index
Type
Table
DB
Table
Table
DB
Table
ESFlute DBFlute
DBFluteフェス 201617
基本的な操作
DBFluteフェス 2016
■ インデックス名を指定してPUTでリクエスト
$ curl -XPUT ‘localhost:9200/company -d ‘{ “settings”: { …(Analyzer等のインデックス関連の指定)... }, “mappings”: { …(スキーマなど指定)... }}’
インデックス作成
18
DBFluteフェス 2016
■ PUT/POSTでJSONオブジェクトをリクエスト■ パスは /インデックス名/タイプ名/ID
$ curl -XPUT ‘localhost:9200/company/employee/1’ -d ‘{ “employee_id”: ”00001”, “first_name”: “Taro”, “last_name”: “Suzuki”}’
ドキュメントの追加
19
DBFluteフェス 2016
■ DELETEでリクエスト■ パスは /インデックス名/タイプ名/ID■ プラグイン導入してクエリーで削除も可能
$ curl -XDELETE ‘localhost:9200/company/employee/1’
ドキュメントの削除
20
DBFluteフェス 2016
■ 文字列: string■ 数値: long, integer, short, byte, double, float■ 日付: date■ 論理値: boolean■ バイナリ: binary■ その他: object, geo_point, ip,...
ESFluteではバイナリやその他の型はまだ未対応
基本的な型
21
DBFluteフェス 2016
■ 文字列を解析して、索引を作る
■ 検索は索引から単語が含まれるIDリストを取得
転置インデックス
22
ID 文字列
1 東京スカイツリー
2 東京タワー
単語 IDリスト
東京 1, 2
スカイツリ 1
タワー 2
DBFluteフェス 2016
■ テキストを分解して単語群を生成する■ CharFilter/Tokenizer/TokenFilterで構成される■ Luceneのインターフェース■ 組み合わせることで自由自在の解析可能■ インデックスの生成時に設定する
「今日の天気は晴れです」
↓「今日」「天気」「晴れ」
Analyzer
23
DBFluteフェス 2016
Analyzer
24
「東京スカイツリーの①番出口」
CharFilter (文字単位で変換)
TokenFilter (単語単位で変換)
Tokenizer (単語に分割)
「東京スカイツリーの1番出口」
「東京」「スカイツリー」「の」「1番」「出口」
「東京」「スカイツリ」「1番」「出口」
Analyzer
DBFluteフェス 2016
■ 構造的なQuery DSLを利用■ HTTPまたはTransportでリクエスト
➔現時点ではESFluteはTransportを利用
■ 様々なクエリーをサポート(aggs, geo,...)
$ curl -XPOST ‘localhost:9200/company/_search -d ‘{ “query” : { "match_phrase" : { "content" : "fess" } }, “size”: 10}
検索クエリー
25
DBFluteフェス 2016
ESFluteの設定
26
DBFluteフェス 2016
■ DBFluteの基本的な設定を作成例(Mavenプロジェクトなら):
$ mvn dbflute:download$ mvn dbflute:create-client
http://dbflute.seasar.org/ja/environment/setup/index.html
DBFluteの環境準備
27
DBFluteフェス 2016
■ ESFluteはインデックス情報を利用■ Elasticsearchのインデックス情報をファイルに保存
する
$ curl -XGET localhost:9200/[index] > dbflute_[project]/playes/index/[index].json
インデックス情報の保存
28
DBFluteフェス 2016
■ dbflute_[project]/dfprop/esfluteMap.dfprop
esfluteMap.dfprop
map:{ # base package of generated classes ; basePackage = org.docksidestage.esflute
# base path to JSON resource, URL or relative path ; basePath = ./playes/index ←インデックスの設定情報ファイルを置いておくディレクトリ
# settings for indexes ; indexMap = map:{ ; maihama = map:{ ←インデックス名
; package = maihama } }
# version for elasticsearch's jar file (no version means latest) ; elasticsearchVersion = 2.3.0}
29
DBFluteフェス 2016
■ DBFluteのFreeGenを実行$ mvn dbflute:freegen
➔ [basePackage].[index]パッケージ以下にソースコードを自動生成
➔ DB版同様、CBやBhv等が生成される
ソースコードの生成
30
DBFluteフェス 2016
必要に応じて…
コンポーネント定義
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE components PUBLIC "-//DBFLUTE//DTD LastaDi 1.0//EN" "http://dbflute.org/meta/lastadi10.dtd"><components> <include path="esclient.xml"/>
<!-- The components of DBFlute Runtime. --> <component name="behaviorCommandInvoker" class="org.dbflute.bhv.core.BehaviorCommandInvoker"/>
<!-- The components of Behavior. --> <component name="userBhv" class="org.codelibs.fess.es.user.exbhv.UserBhv"/> <component name="roleBhv" class="org.codelibs.fess.es.user.exbhv.RoleBhv"/> <component name="groupBhv" class="org.codelibs.fess.es.user.exbhv.GroupBhv"/></components>
31
DBFluteフェス 2016
ESFluteで操作
32
DBFluteフェス 2016
■ Bhvインスタンスでinsert➔ オプションで即時反映も可能➔ 更新も同じ感じで
データの追加
@Resourceprivate ProductBhv productBhv;…
Product product = new Product();product.setProductDescription(form.productDescription);product.setProductCategoryCode(form.productCategoryCode);...productBhv.insert(product, op -> { op.setRefresh(true); // 即時反映
});
33
DBFluteフェス 2016
■ Bhvインスタンスでdelete➔ オプションで即時反映も可能➔ Delete By Queryも内部的にScrollで実現
データの削除
productBhv.selectByPK(form.productId).ifPresent(entity -> { productBhv.delete(entity, op -> { op.setRefresh(true); });}).orElse(() -> { throw404("Not found the product: " + form.productId);});
34
DBFluteフェス 2016
■ 各種検索クエリーに対応➔ConditionBeanがQuery DSLを組み立てる
■ Aggregationにも対応
検索
35
DBFluteフェス 2016
検索
36
DBFluteフェス 2016
■ BhvでselectPage, selectList, selectByPK, selectCount, selectEntity, selectCursorに対応
■ CBで検索条件を指定➔ pagingやaddOrder〜も対応
■ 戻り値はDBFluteと同様
検索系メソッド
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.query().addOrderBy_Id_Asc(); cb.paging(5, 1);});
37
DBFluteフェス 2016
■ 全件にマッチするクエリー➔ count等で利用
Match All Query
38
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.query().addOrderBy_Id_Asc(); cb.paging(5, 1);});
DBFluteフェス 2016
■ 解析された文字列にマッチするクエリー➔ 全文検索で利用
■ 解析された文字列にマッチするので、以下のfluteはFluteにも一致する➔ フレーズとしてはマッチしない➔ 日本語bi-gramでは期待通りの結果にならない
Match Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().setProductName_Match("flute"); cb.query().addOrderBy_Id_Asc(); cb.paging(5, 1);});
39
DBFluteフェス 2016
■ 解析された文字列のフレーズでマッチする➔ タームの順番も含めて一致する➔ 日本語の場合、ほぼこのクエリーを利用する➔ Match Queryの場合、並び順に関係なく、ヒットする
Match Phrase Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().setProductName_MatchPhrase("Low Price Flute"); cb.query().addOrderBy_Id_Asc(); cb.paging(5, 1);});
40
DBFluteフェス 2016
■ フレーズでの前方一致にマッチするクエリー➔ Match Phraseの前方一致版クエリー➔ 入力された文字列の最後の単語では前方一致を行う
Match Phrase Prefix Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().setProductName_MatchPhrasePrefix("Low P", op -> { op.maxExpansions(10); }); cb.query().addOrderBy_Id_Asc(); cb.paging(5, 1);});
41
DBFluteフェス 2016
■ 頻出単語を除外してマッチするクエリー■ 頻度の指定が可能
➔ 以下の例だと、0.1%より多く登場する単語を除外
Common Terms Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().setProductName_CommonTerms("What is Grand Piano", op -> { op.cutoffFrequency(0.001f); }); cb.query().addOrderBy_Id_Asc(); cb.paging(5, 1);});
42
DBFluteフェス 2016
■ 検索クエリーを文字列で指定するクエリー➔ サーチエンジンに利用➔ Luceneの構文に従う
■ 入力によって幅広い検索が可能➔ 範囲検索、あいまい検索、正規表現等
Query String Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().queryString("Flute OR Piano", op -> { op.defaultField("product_name"); }); cb.query().addOrderBy_Id_Asc(); cb.paging(5, 1);});
43
DBFluteフェス 2016
■ 指定された値にマッチするクエリー➔ テキスト解析はされず、値そのものにマッチする➔ 区分値など、値のまま、インデックスしたものの検索➔ not_analyzedのフィールドに対して利用➔ set〜_EqualはTerm Queryを利用
Term Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().setProductHandleCode_Term("FLUTE-01", op -> { op.queryName("exact_value"); }); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
44
DBFluteフェス 2016
■ Term Queryの複数指定版クエリー➔ 複数の値にマッチする検索をする➔ set〜_InScopeはTerms Queryを利用
Terms Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { ArrayList<String> terms = new ArrayList<>(); terms.add("piano"); terms.add("flute"); cb.query().setProductName_Terms(terms); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
45
DBFluteフェス 2016
■ 範囲指定検索➔ 数値や日付を範囲指定する場合に利用する➔ 通常のCBと同様
Range Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().setLatestPurchaseDate_GreaterEqual( LocalDateTime.of(2014, 1, 1, 0, 0, 0)); cb.query().addOrderBy_LatestPurchaseDate_Asc(); cb.paging(5, 1);});
46
DBFluteフェス 2016
■ フィールドに値が存在するものにマッチする➔ 値があるものを検索する場合に利用する
■ 存在しないものを検索したい場合➔ boolのmust_notのexistsクエリーを利用する
■ null値はElasticsearch上で設定可能
Exists Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().setProductName_Exists(); cb.query().addOrderBy_ProductName_Asc(); cb.paging(5, 1);});
47
DBFluteフェス 2016
■ 前方一致のクエリー➔ Term Queryの前方一致検索版➔ テキスト解析されないフィールドで利用する
Prefix Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().setProductCategory_Prefix("Ins"); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
48
DBFluteフェス 2016
■ ワイルドカードによる検索➔ Term Queryのワイルドカード検索版➔ テキスト解析されないフィールドで利用する
Wildcard Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().setProductCategoryCode_Wildcard("H?B"); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
49
DBFluteフェス 2016
■ 正規表現による検索➔ Term Queryで正規表現を利用したい場合に利用する➔ 遅い
Regexp Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().setProductHandleCode_Regexp("[A-Z]{5}-<01-02>"); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
50
DBFluteフェス 2016
■ あいまい検索➔ レーベンシュタイン距離による検索➔ 類似する単語を見つける
Fuzzy Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().setProductCategory_Fuzzy("Insturument"); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
51
DBFluteフェス 2016
■ and/or/notを合成するクエリー➔ and条件: mustで指定する(複数指定可能)➔ or条件: shouldで指定する(複数指定可能)➔ not条件: mustNotで指定する(複数指定可能)
■ filter条件も設定可能➔ and/or/notの前にフィルタ条件を指定する
Bool Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().bool((must, should, mustNot, filter) -> { must.setProductName_Match("flute"); mustNot.setProductName_Match("gold"); }); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
52
DBFluteフェス 2016
■ スコアを調整するクエリー➔ functionsにマッチしたものをもとにスコアを変える➔ queryには通常の検索条件を指定する
Function Score Query
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().functionScore(query -> { query.setProductCategory_Equal("MusicCD"); }, functions -> { functions.filter(cq -> { cq.setProductName_Match("street"); }, ScoreFunctionBuilders.weightFactorFunction(10.0f)); }, op -> {}); cb.paging(5, 1);});
53
DBFluteフェス 2016
Aggregation
54
DBFluteフェス 2016
■ 検索時に集計処理も同時に行う➔ ファセットとかドリルダウンとか
■ 集計結果はElasticsearchのインスタンスを利用➔ 独自に生成するとメンテリスクがあるため
Aggregation
55
DBFluteフェス 2016
■ 平均値を集計する➔ Avgのインスタンスが返却される
Avg Aggregation
56
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.aggregation().setRegularPrice_Avg(); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
Avg avg = (Avg) aggregations.get("regular_price");assertEquals(353455.0, avg.getValue());
DBFluteフェス 2016
■ フィールドでユニークな項目数を取得する➔ Cardinalityが返却される
Cardinality Aggregation
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.aggregation().setProductCategory_Cardinality(); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
Cardinality cardinality = (Cardinality) aggregations.get("product_category");assertEquals(3, cardinality.getValue());
57
DBFluteフェス 2016
■ 様々な統計的数値を得る➔ ExtendedStatsが返却される➔ 最大、最小、平均、分散、標準偏差等
Extended Stats Aggregation
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.aggregation().setRegularPrice_ExtendedStats(); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
ExtendedStats extendedStats = (ExtendedStats) aggregations.get("regular_price");assertEquals(353455.0, extendedStats.getAvg());
58
DBFluteフェス 2016
■ 最大値を取得する➔ Maxが返却される
Max Aggregation
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.aggregation().setRegularPrice_Max(); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
Max max = (Max) aggregations.get("regular_price");assertEquals(4000000.0, max.getValue());
59
DBFluteフェス 2016
■ 最小値を取得する➔ Minが返却される
Min Aggregation
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.aggregation().setRegularPrice_Min(); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
Min min = (Min) aggregations.get("regular_price");assertEquals(340.0, min.getValue());
60
DBFluteフェス 2016
■ 百分位数を計算する➔ 小さい順に並べて、X%にある値を取得する➔ 外れ値を検出するなど➔ 以下ではRegularPriceの下位1%の位置に347.6
Percentiles Aggregation
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.aggregation().setRegularPrice_Percentiles(); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
Percentiles percentiles = (Percentiles) aggregations.get("regular_price");assertEquals(347.6, percentiles.percentile(1));
61
DBFluteフェス 2016
■ 百分位数における順位を計算する➔ 指定した値のランクを取得する➔ 以下ではRegularPriceが1000のときが23.125%
Percentile Ranks Aggregation
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.aggregation().setRegularPrice_PercentileRanks(op -> op.percentiles(1000, 10000)); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
PercentileRanks percentileRanks = (PercentileRanks) aggregations.get("regular_price");assertEquals(23.125, percentileRanks.percent(1000));
62
DBFluteフェス 2016
■ 基本的な統計的数値を算出する➔ Statsが返却される➔ 項目数、最小値、最大値、平均値、合計値
Stats Aggregation
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.aggregation().setRegularPrice_Stats(); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
Stats stats = (Stats) aggregations.get("regular_price");assertEquals(353455.0, stats.getAvg());
63
DBFluteフェス 2016
■ 合計値を取得する➔ Sumが返却される
Sum Aggregation
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.aggregation().setRegularPrice_Sum(); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
Sum sum = (Sum) aggregations.get("regular_price");assertEquals(7069100.0, sum.getValue());
64
DBFluteフェス 2016
■ フィールドに値を持つ件数を取得する➔ ValueCountが返却される➔ スクリプトによる指定も可能
Value Count Aggregation
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.aggregation().setProductCategory_Count(); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
ValueCount valueCount = (ValueCount) aggregations.get("product_category");assertEquals(20, valueCount.getValue());
65
DBFluteフェス 2016
■ 指定したフィルタで項目を絞り込む➔ 子のAggregationをフィルタするときに利用
■➔➔
Filter Aggregation
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.aggregation().filter("filter", cq -> cq.setProductName_Equal("flute"), op -> {}, aggs -> aggs.setRegularPrice_Avg()); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
Filter filter = (Filter) aggregations.get("filter");Avg avg = (Avg) filter.getAggregations().get("regular_price");assertEquals(1516666.6666666667, avg.getValue());
66
DBFluteフェス 2016
■ ヒストグラムを生成する➔ Histogramが返却される
Histogram Aggregation
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().setRegularPrice_LessThan(2000); cb.aggregation().setRegularPrice_Histogram(op -> { op.interval(200).extendedBounds(0L, 2000L); }, aggs -> {}); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
Histogram histogram = (Histogram) aggregations.get("regular_price");assertEquals(11, histogram.getBuckets().size());assertEquals(3L, histogram.getBuckets().get(1).getDocCount());
67
DBFluteフェス 2016
■ フィールドに値が存在しない項目を得る➔ Missingが返却される
Missing Aggregation
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.aggregation().setRegularPrice_Missing(); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
Missing missing = (Missing) aggregations.get("regular_price");assertEquals(0, missing.getDocCount());
68
DBFluteフェス 2016
■ フィールド値が範囲内にある件数を取得する➔ Rangeが返却される
Range Aggregation
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.aggregation().setRegularPrice_Range(op -> { op.addUnboundedFrom("all", 0) .addRange("average", 1000, 5000) .addUnboundedTo("cheap", 1000); }, aggs -> {}); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});
EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
Range range = (Range) aggregations.get("regular_price");assertEquals(4, range.getBuckets().get(0).getDocCount());
69
DBFluteフェス 2016
■ 指定した単語が含まれる件数を取得する➔ Termsが返却される➔ 含まれる単語と件数が取得できる
Terms Aggregation
PagingResultBean<Product> list1 = productBhv.selectPage(cb -> { cb.query().matchAll(); cb.aggregation().setProductName_Terms(); cb.query().addOrderBy_ProductHandleCode_Asc(); cb.paging(5, 1);});EsPagingResultBean<Product> list2 = (EsPagingResultBean<Product>) list1;Aggregations aggregations = list2.getAggregations();
Terms terms = (Terms) aggregations.get("product_name");List<Bucket> buckets = terms.getBuckets();assertEquals("100g", buckets.get(0).getKey());assertEquals(4, buckets.get(0).getDocCount());
70
DBFluteフェス 2016
■ Elasticsearch 5.0対応■ geo_point対応■ ネストしたプロパティへの対応■ multifieldの対応■ join対応■ HTTPでの接続方法■ 適切でない利用方法やクエリを通知する
今後のタスク
71
DBFluteフェス 2016
まとめ
72
DBFluteフェス 2016
■ ESFluteでElasticsearchを利用できる■ DBFluteのように実装できる■ インデックス情報からソースは自動生成■ DBFluteにバンドル済みなので、すぐに利用可能
まとめ
73