ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

73
ESFluteによるElasticsearchでの O/Rマッパーを用いた開発 DBFluteフェス2016 1

Transcript of ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

Page 1: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス2016 1

Page 2: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ 名前: 菅谷信介■ オープンソース活動:

➔ Fess, DBFlute関連, Apache Portals, Codehaus, Seasar2,...

■ Blog: http://www.chazine.com/■ Twitter: https://twitter.com/shinsuke_sugaya/

自己紹介

2

Page 3: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ ESFluteとは■ Elasticsearchについて■ ESFluteの使い方

アジェンダ

3

Page 4: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

背景

4

Page 5: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ 全文検索サーバFessでDBFluteを用いて開発➔検索データはSolrで管理➔設定データはデータベースで管理

■ SolrからElasticsearchに移行➔この機会にDBをやめることを決断➔DBがあると構築の手間が増える

■ 大量のBhvやCBを利用したコードが存在➔工数が…

ESFlute誕生の背景

5

Page 6: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

BhvやCBでElasticsearchにアクセスできればよいのでは!

6

Page 7: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

ESFluteとは

7

Page 8: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ DBFluteのElasticsearch版➔ CBやBhvが使えます

■ インデックス設定情報からソースの自動生成■ Elasticsearch 1.7と2.xに対応

➔ 5.x系対応は近日対応予定➔ バージョンごとにブランチ管理

■ DBFlute本体にバンドル➔ 将来的には単独の配布にしたい

■ Exampleプロジェクトhttps://github.com/lastaflute/lastaflute-example-waterfrontテストコードもあります!

ESFluteとは

8

Page 9: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

Elasticsearchとは

9

Page 10: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ OSSの分散リアルタイム検索&分析エンジン■ 特徴

➔ドキュメント指向な検索エンジン(Apache Lucene)➔RESTfulなAPI➔スキーマフリー➔分散システム (高可用性、スケールアウト)➔プラグインによる拡張

Elasticsearchとは

10

Page 11: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

(物理的な?)構成要素

ESFlute的にはあまり気にしないで良いかも…

11

Page 12: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

分散環境的な話…

■ 基本的な構成要素➔ クラスタ➔ ノード➔ インデックス➔ シャード

■ クラスタは複数のノードから構成される■ クラスタは複数のインデックスを保持できる■ インデックスは複数のシャードから構成される■ シャードは1つのプライマリと複数のレプリカで構成

される■ ノードは複数のシャードを保持できる

構成要素

12

Page 13: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

クラスタ

Page 14: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

(論理的な?)構成要素

ESFlute的には自動生成上、関係ある

14

Page 15: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ インデックス単位にドキュメントが保存される■ ドキュメントはType属性を持つ➔ドキュメントはタイプに含まれる

■ 検索はインデックスとタイプを指定する

データ観点での構成要素

15

Type

Index

Cluster

Type

Type

Index

Type

Page 16: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ ESFluteはDBFluteのようなO/Rマッパー■ インデックスを1つのデータベースとして考える■ タイプをテーブルとして考える

ESFluteでの構成要素の対応

16

Type

Index

Cluster

Type

Type

Index

Type

Table

DB

Table

Table

DB

Table

ESFlute DBFlute

Page 17: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 201617

基本的な操作

Page 18: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ インデックス名を指定してPUTでリクエスト

$ curl -XPUT ‘localhost:9200/company -d ‘{ “settings”: { …(Analyzer等のインデックス関連の指定)... }, “mappings”: { …(スキーマなど指定)... }}’

インデックス作成

18

Page 19: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ PUT/POSTでJSONオブジェクトをリクエスト■ パスは /インデックス名/タイプ名/ID

$ curl -XPUT ‘localhost:9200/company/employee/1’ -d ‘{ “employee_id”: ”00001”, “first_name”: “Taro”, “last_name”: “Suzuki”}’

ドキュメントの追加

19

Page 20: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ DELETEでリクエスト■ パスは /インデックス名/タイプ名/ID■ プラグイン導入してクエリーで削除も可能

$ curl -XDELETE ‘localhost:9200/company/employee/1’

ドキュメントの削除

20

Page 21: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ 文字列: string■ 数値: long, integer, short, byte, double, float■ 日付: date■ 論理値: boolean■ バイナリ: binary■ その他: object, geo_point, ip,...

ESFluteではバイナリやその他の型はまだ未対応

基本的な型

21

Page 22: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ 文字列を解析して、索引を作る

■ 検索は索引から単語が含まれるIDリストを取得

転置インデックス

22

ID 文字列

1 東京スカイツリー

2 東京タワー

単語 IDリスト

東京 1, 2

スカイツリ 1

タワー 2

Page 23: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ テキストを分解して単語群を生成する■ CharFilter/Tokenizer/TokenFilterで構成される■ Luceneのインターフェース■ 組み合わせることで自由自在の解析可能■ インデックスの生成時に設定する

「今日の天気は晴れです」

     ↓「今日」「天気」「晴れ」

Analyzer

23

Page 24: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

Analyzer

24

「東京スカイツリーの①番出口」

CharFilter (文字単位で変換)

TokenFilter (単語単位で変換)

Tokenizer (単語に分割)

「東京スカイツリーの1番出口」

「東京」「スカイツリー」「の」「1番」「出口」

「東京」「スカイツリ」「1番」「出口」

Analyzer

Page 25: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 26: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

ESFluteの設定

26

Page 27: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ DBFluteの基本的な設定を作成例(Mavenプロジェクトなら):

$ mvn dbflute:download$ mvn dbflute:create-client

http://dbflute.seasar.org/ja/environment/setup/index.html

DBFluteの環境準備

27

Page 28: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ ESFluteはインデックス情報を利用■ Elasticsearchのインデックス情報をファイルに保存

する

$ curl -XGET localhost:9200/[index] > dbflute_[project]/playes/index/[index].json

インデックス情報の保存

28

Page 29: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 30: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ DBFluteのFreeGenを実行$ mvn dbflute:freegen

➔ [basePackage].[index]パッケージ以下にソースコードを自動生成

➔ DB版同様、CBやBhv等が生成される

ソースコードの生成

30

Page 31: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 32: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

ESFluteで操作

32

Page 33: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 34: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 35: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ 各種検索クエリーに対応➔ConditionBeanがQuery DSLを組み立てる

■ Aggregationにも対応

検索

35

Page 36: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

検索

36

Page 37: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 38: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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);});

Page 39: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 40: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 41: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 42: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 43: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 44: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 45: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 46: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 47: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 48: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 49: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 50: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 51: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 52: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 53: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 54: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

Aggregation

54

Page 55: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ 検索時に集計処理も同時に行う➔ ファセットとかドリルダウンとか

■ 集計結果はElasticsearchのインスタンスを利用➔ 独自に生成するとメンテリスクがあるため

Aggregation

55

Page 56: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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());

Page 57: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 58: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 59: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 60: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 61: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 62: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 63: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 64: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 65: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 66: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 67: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 68: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 69: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 70: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

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

Page 71: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ Elasticsearch 5.0対応■ geo_point対応■ ネストしたプロパティへの対応■ multifieldの対応■ join対応■ HTTPでの接続方法■ 適切でない利用方法やクエリを通知する

今後のタスク

71

Page 72: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

まとめ

72

Page 73: ESFluteによるElasticsearchでのO/Rマッパーを用いた開発

DBFluteフェス 2016

■ ESFluteでElasticsearchを利用できる■ DBFluteのように実装できる■ インデックス情報からソースは自動生成■ DBFluteにバンドル済みなので、すぐに利用可能

まとめ

73