Elasticsearchでインデックスを作成する
インデックスを作成するにあたり、mappingsやsettingsあたりを一緒に登録する方法。
インデックス作成
curl -H "Content-Type: application/json" -XPUT "http://localhost:9200/sample_index?pretty" -d ' { "settings": { "number_of_shards": 100 }, "mappings": { "properties": { "sample": { "type": "text" } } } } '
mappings確認
期待通りか確認する。
curl -XGET "http://localhost:9200/sample_index/_mappings?pretty" { "sample_index" : { "mappings" : { "properties" : { "sample" : { "type" : "text" } } } } }
大丈夫そう。
settings確認
次にsettingsも。
curl -XGET "http://localhost:9200/sample_index/_settings?pretty" { "sample_index" : { "settings" : { "index" : { "creation_date" : "1595507722172", "number_of_shards" : "100", "number_of_replicas" : "1", "uuid" : "lCuqDV3KQZi_YuwM-mfx4g", "version" : { "created" : "7080099" }, "provided_name" : "sample_index" } } } }
こちらも大丈夫そう。
最後に
データを投入するとmappingsは自動で作成されるが、事前に定義することも可能であることがわかった。 またsettingsについても定義できることがわかった。今後kuromojiを使用して形態素解析を行う予定なので、その場合はこちらにkuromojiを設定することになるので事前に素振りできてよかった。
Elasticsearchにエイリアスを設定する
ESのインデックス作成後にマッピングの追加はできるけど変更はできないらしい。
ただ、プロダクトの状況によってはインデックスを変更する機会はあるはず。
その場合にどうしたらいいのかを調べていたらエイリアスというものを知ったのでメモ。
エイリアスを設定する理由
プロダクト(ESを参照するサービス)からESのインデックスを直接参照してもいいけど、インデックスはマッピングを変更することができず再作成となる。その場合、プロダクトはインデックスの参照先を切り替えるリリースが発生するため効率的でない。
エイリアスを作成し、プロダクトがそれを参照することでインデックスのマッピングが変更(インデックス再作成)となったとしても、インデックスに設定したエイリアスを変更するだけで、参照先を変更することが不要となる。
ただし、エイリアス宛にドキュメントは登録できないのでプロダクト側の登録先のインデックスを変更する必要は生じる。
エイリアス作成
わかりやすくbank_aliasとする。
curl -H "Content-Type: application/json" -XPOST "http://localhost:9200/_aliases" -d ' { "actions" : [ { "add" : { "index": "bank", "alias": "bank_alias" } } ] }'
エイリアス確認
curl -XGET "http://localhost:9200/_alias?pretty=true"
インデックスを指定することも可能。
curl -XGET "http://localhost:9200/bank/_alias?pretty=true"
エイリアス変更
新しくインデックスを作成する。
curl -XPUT "http://localhost:9200/bank_v2?pretty"
この時点のエイリアスを確認する。
curl -XGET "http://localhost:9200/_alias?pretty=true" { "bank" : { "aliases" : { "bank_alias" : { } } }, "bank_v2" : { "aliases" : { } } }
bank_v2にはエイリアスが設定されていないことが確認できる。
エイリアスを変更する。
curl -H "Content-Type: application/json" -XPOST "http://localhost:9200/_aliases" -d ' { "actions" : [ {"remove": {"index": "bank", "alias": "bank_alias" }}, {"add" : {"index": "bank_v2", "alias": "bank_alias" }} ] }'
エイリアスが変更されたことが確認できる。
curl -XGET "http://localhost:9200/_alias?pretty=true" { "bank" : { "aliases" : { } }, "bank_v2" : { "aliases" : { "bank_alias" : { } } } }
余談
すでに作成済みのエイリアス名でインデックスを作成するとエラーになる。
curl -XPUT "http://localhost:9200/bank_alias?pretty" { "error" : { "root_cause" : [ { "type" : "invalid_index_name_exception", "reason" : "Invalid index name [bank_alias], already exists as alias", "index_uuid" : "_na_", "index" : "bank_alias" } ], "type" : "invalid_index_name_exception", "reason" : "Invalid index name [bank_alias], already exists as alias", "index_uuid" : "_na_", "index" : "bank_alias" }, "status" : 400 }
複数のインデックスに同じエイリアスを設定することも可能。
この場合、1つのエイリアスの検索に対し複数のインデックスから合致する内容が取得できる。
curl -H "Content-Type: application/json" -XPOST "http://localhost:9200/_aliases" -d ' { "actions" : [ {"add" : {"index": "bank", "alias": "bank_alias" }} ] }'
curl -XGET "http://localhost:9200/_alias?pretty=true" { "bank" : { "aliases" : { "bank_alias" : { } } }, "bank_v2" : { "aliases" : { "bank_alias" : { } } } }
最後に
Getting Started with Elasticsearch
ESを使用することになったので、このあたりを見ながら簡単にメモを残す。
環境構築
Dockerを利用したのでdocker-compose.ymlを以下のように設定した。
version: '3' services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.8.0 container_name: elasticsearch environment: - TZ=Asia/Tokyo - discovery.type=single-node ports: - 9200:9200
実行方法は以下。
docker-compose up -d
起動後したか確認する。
curl -XGET "http://localhost:9200/_cat/health?v"
インデックス
ESではインデックスの中にドキュメント(データ)を登録していく。
※RDBのテーブル、レコードのようなイメージ
インデックス作成
curl -XPUT "http://localhost:9200/customer?pretty"
登録済みのインデックスを確認する
curl -XGET "http://localhost:9200/_cat/indices?v"
インデックス削除
curl -XDELETE "http://localhost:9200/customer?pretty"
すべてのインデックスを一括で削除することも可能。
curl -XDELETE "http://localhost:9200/*"
ドキュメント
インデックス内に登録するデータをドキュメントと呼ぶ。
ドキュメントの登録
ホスト/インデックス/タイプ
の順で指定する。
タイプについては、ES7ではインデックスに複数のタイプが登録できないようなので、_doc固定で良いのかも。
curl -H "Content-Type: application/json" -XPOST "http://localhost:9200/customer/_doc?pretty" -d '{"customer": "John Doe"}'
なお、ホスト/インデックス/タイプ/ID
の順で指定することで登録することも可能。
この場合は、POSTでなくPUTとなる。
curl -H "Content-Type: application/json" -XPUT "http://localhost:9200/customer/_doc/1?pretty" -d '{"customer": "John Doe"}'
ドキュメント取得
登録時のレスポンスに含まれる"_id"を指定する。
curl -XGET "http://localhost:9200/customer/_doc/<_idの値>?pretty"
ドキュメント削除
登録時のレスポンスに含まれる"_id"を指定する。
curl -XDELETE "http://localhost:9200/customer/_doc/<_idの値>?pretty"
大量データ登録
もちろん、大量データ登録も可能。
こちらからaccounts.jsonのデータをダウンロードして登録する。
curl -H "Content-Type: application/json" -XPOST "http://localhost:9200/bank/_bulk?pretty&refresh" --data-binary "@accounts.json"
検索
ESの醍醐味であろう検索。
Queryを駆使することで柔軟な検索が可能になる。
特定のフィールド検索
スペースで区切ることでOR検索が可能。
curl -H "Content-Type: application/json" -XGET "http://localhost:9200/bank/_search?pretty" -d ' { "query": { "match": { "address": "mill lane" } } }'
複雑な検索
Boolean queryを使用すると複雑な検索も可能。
それぞれがTrueになるドキュメントを取得する。
詳細はこちら
curl -H "Content-Type: application/json" -XGET "http://localhost:9200/bank/_search?pretty" -d ' { "query": { "bool": { "must": [ { "match": { "age": "40" } } ], "must_not": [ { "match": { "state": "ID" } } ] } } }'
範囲指定はこうする。
curl -H "Content-Type: application/json" -XGET "http://localhost:9200/bank/_search?pretty" -d ' { "query": { "bool": { "must": { "match_all": {} }, "filter": { "range": { "balance": { "gte": 20000, "lte": 30000 } } } } } }'
集約
Groupingすることも可能。
curl -H "Content-Type: application/json" -XGET "http://localhost:9200/bank/_search?pretty" -d ' { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" } } } }'
ソート指定
curl -H "Content-Type: application/json" -XGET "http://localhost:9200/bank/_search?pretty" -d ' { "query": { "match_all": {} }, "sort": [ { "account_number": "asc" } ] }'
RDBのようにOFFSET, LIMITを指定することも可能。
curl -H "Content-Type: application/json" -XGET "http://localhost:9200/bank/_search?pretty" -d ' { "query": { "match_all": {} }, "sort": [ { "account_number": "asc" } ], "from": 10, "size": 10 }'
最後に
以上を知っておけばとりあえずESを完全に理解した気になれる。
ここからは先は状況に応じてリファレンスをピックアップしていくのが良さそう。
Ruby on Jets デプロイ
デプロイユーザーのクレデンシャル、デプロイ先のリージョンを指定してjets deploy
する。
docker-compose run --rm -e AWS_ACCESS_KEY_ID=unk -e AWS_SECRET_ACCESS_KEY=unk -e AWS_REGION=ap-northeast-1 app bundle exec jets deploy
Ruby on Jets Dockerfile修正
以下の記事でDockerfileをalpineイメージから作成していたがデプロイでapply2files not supportedのようなエラーが発生するためbusterイメージに変更した。 (エラーメッセージは失念…)
FROM node:12.16.0-buster as builder FROM ruby:2.5-buster RUN apt-get update && \ apt-get install -y default-mysql-client rsync zip RUN useradd -m jets && \ gpasswd -a jets sudo && \ echo "jets:jets" | chpasswd ENV YARN_VERSION=1.22.0 COPY --from=builder /opt/yarn-v$YARN_VERSION /opt/yarn/ COPY --from=builder /usr/local/bin/node /usr/local/bin/ RUN ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn && \ ln -s /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg USER jets ENV APP_HOME=/app WORKDIR $APP_HOME COPY ./Gemfile* $APP_HOME/ RUN bundle install
Ruby on Jets チュートリアル(CRUDの作成)
Ruby on Jets デモプロジェクト作成後、 http://localhost:8888 に表示される手順に沿ってpostsのCRUDを作成する。
jets generate scaffold post title:string
jets db:create db:migrate
jets server
open http://localhost:8888/posts
jets help
MySQLコンテナの作成
MySQLを使用するためdocker-compose.ymlを修正する。
Ruby on Jetsのプロジェクトを作成した際に.env.developmentファイルが作成されるのでそれを読み込むようにする。
version: '3' services: app: build: context: . dockerfile: ./docker/ruby/Dockerfile volumes: - .:/app - bundle-data:/usr/local/bundle ports: - "8888:8888" depends_on: - db command: bundle exec jets server --port 8888 --host 0.0.0.0 db: image: mysql:5.7 ports: - "3306:3306" env_file: - .env.development volumes: bundle-data: driver: local
.env.developmentに以下を追加する。
MYSQL_RANDOM_ROOT_PASSWORD=yes MYSQL_DATABASE=jets_db MYSQL_USER=jets_user MYSQL_PASSWORD=jets_password DB_HOST=db # docker-composeで定義したサービス名
config/database.ymlを修正する(database, username, password)。
default: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV["DB_POOL"] || 5 %> database: <%= ENV['MYSQL_DATABASE'] || 'app_development' %> username: <%= ENV['MYSQL_USER'] || 'root' %> password: <%= ENV['MYSQL_PASSWORD'] %> host: <%= ENV["DB_HOST"] %> url: <%= ENV['DATABASE_URL'] %> # takes higher precedence than other settings reconnect: true development: <<: *default database: <%= ENV['MYSQL_DATABASE'] || 'app_development' %> test: <<: *default database: app_test production: <<: *default database: app_production url: <%= ENV['DATABASE_URL'] %>
Jets generate and migrate
あとは一番上の手順を参考にコマンドを実行し、http://localhost:8888/posts にアクセスする。
docker-compose run --rm app bundle exec jets generate scaffold post title:string docker-compose run --rm app bundle exec jets db:migrate # MySQLコンテナ作成時にdatabaseも作成するため、db:createは不要 docker-compose up open http://localhost:8888/posts docker-compose exec app bundle exec jets help # コマンド一覧
以下の画面が表示される。
Ruby on Jets デモプロジェクト作成
Ruby on Jets 環境構築に続き、プロジェクトを作成する。
プロジェクト作成
カレントディレクトリにプロジェクトを作成する。
docker-compose run --rm app bundle exec jets new .
rootでの実行とgitコマンドについてエラーメッセージが出るので、Dockerfileを修正する。
Don't run Bundler as root. Bundler can ask for sudo if it is needed, and installing your bundle as root will break this application for all non-root users on this machine. Fetching https://github.com/tongueroo/webpacker.git sh: git: not found Retrying `git clone 'https://github.com/tongueroo/webpacker.git' "/usr/local/bundle/cache/bundler/git/webpacker-34bfa510f2df7739e222f010552da83abb125db1" --bare --no-hardlinks --quiet` due to error (2/4): Bundler::Source::Git::GitCommandError Git error: command `git clone 'https://github.com/tongueroo/webpacker.git' "/usr/local/bundle/cache/bundler/git/webpacker-34bfa510f2df7739e222f010552da83abb125db1" --bare --no-hardlinks --quiet` in directory /app has failed.sh: git: not found Retrying `git clone 'https://github.com/tongueroo/webpacker.git' "/usr/local/bundle/cache/bundler/git/webpacker-34bfa510f2df7739e222f010552da83abb125db1" --bare --no-hardlinks --quiet` due to error (3/4): Bundler::Source::Git::GitCommandError Git error: command `git clone 'https://github.com/tongueroo/webpacker.git' "/usr/local/bundle/cache/bundler/git/webpacker-34bfa510f2df7739e222f010552da83abb125db1" --bare --no-hardlinks --quiet` in directory /app has failed.sh: git: not found Retrying `git clone 'https://github.com/tongueroo/webpacker.git' "/usr/local/bundle/cache/bundler/git/webpacker-34bfa510f2df7739e222f010552da83abb125db1" --bare --no-hardlinks --quiet` due to error (4/4): Bundler::Source::Git::GitCommandError Git error: command `git clone 'https://github.com/tongueroo/webpacker.git' "/usr/local/bundle/cache/bundler/git/webpacker-34bfa510f2df7739e222f010552da83abb125db1" --bare --no-hardlinks --quiet` in directory /app has failed.sh: git: not found Git error: command `git clone 'https://github.com/tongueroo/webpacker.git' "/usr/local/bundle/cache/bundler/git/webpacker-34bfa510f2df7739e222f010552da83abb125db1" --bare --no-hardlinks --quiet` in directory /app has failed.
FROM ruby:2.5-alpine RUN apk update && \ apk add --no-cache build-base libxml2-dev libxslt-dev git RUN adduser -S jets && \ echo "jets ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \ echo 'jets:jets' | chpasswd USER jets ENV APP_HOME=/app WORKDIR $APP_HOME COPY ./Gemfile* $APP_HOME/ RUN bundle install
Dockerfileの修正後、再度ビルドを実行する。
docker-compose build docker-compose run --rm app git --version git version 2.24.1 docker-compose run --rm app whoami jets
カレントディレクトリにプロジェクトを作成する。 README.md、Gemfileを既に作成しているため、forceオプションを指定する。
docker-compose run --rm app bundle exec jets new . --force
MySQLインストールエラー
MySQLに関するエラーが発生した。
Gem::Ext::BuildError: ERROR: Failed to build gem native extension. current directory: /usr/local/bundle/gems/mysql2-0.5.3/ext/mysql2 /usr/local/bin/ruby -I /usr/local/lib/ruby/site_ruby/2.5.0 -r ./siteconf20200119-7-19w4mew.rb extconf.rb checking for rb_absint_size()... yes checking for rb_absint_singlebit_p()... yes checking for rb_wait_for_single_fd()... yes checking for -lmysqlclient... no ----- mysql client is missing. You may need to 'sudo apt-get install libmariadb-dev', 'sudo apt-get install libmysqlclient-dev' or 'sudo yum install mysql-devel', and try again. -----
ライブラリが足りていなかったので追加する。
FROM ruby:2.5-alpine RUN apk update && \ apk add --no-cache build-base libxml2-dev libxslt-dev git mariadb-dev RUN adduser -S jets && \ echo "jets ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \ echo 'jets:jets' | chpasswd USER jets ENV APP_HOME=/app WORKDIR $APP_HOME COPY ./Gemfile* $APP_HOME/ RUN bundle install
Dockerfileの修正後、再度ビルドを実行する。
docker-compose build docker-compose run --rm app bundle exec jets new . --force
Yarnインストール
Yarnのインストールを行う必要がある。
Yarn is not installed or has not been detected. Please double check that yarn has been installed.
To check:
which yarn
If it is not installed, you can usually install it with:
npm install -g yarn
Dockerfileを修正する。
FROM node:alpine as builder FROM ruby:2.5-alpine RUN apk update && \ apk add --no-cache build-base libxml2-dev libxslt-dev git mariadb-dev RUN adduser -S jets && \ echo "jets ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \ echo 'jets:jets' | chpasswd ENV YARN_VERSION=1.21.1 COPY --from=builder /opt/yarn-v$YARN_VERSION /opt/yarn/ COPY --from=builder /usr/local/bin/node /usr/local/bin/ RUN ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn && \ ln -s /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg USER jets ENV APP_HOME=/app WORKDIR $APP_HOME COPY ./Gemfile* $APP_HOME/ RUN bundle install
Dockerfileの修正後、再度ビルドを実行する。
docker-compose build
docker-compose run --rm app which yarn
docker-compose run --rm app yarn --version
Yarnのインストール確認後、再度プロジェクトを作成する。
docker-compose run --rm app bundle exec jets new . --force
以下のメッセージが表示されたら完了。
================================================================ Congrats 🎉 You have successfully created a Jets project. Cd into the project directory: cd . To start a server and test locally: jets server # localhost:8888 should have the Jets welcome page Scaffold example: jets generate scaffold post title:string body:text published:boolean jets db:create db:migrate To deploy to AWS Lambda, edit your .env.development.remote and add a DATABASE_URL endpoint. Then run: jets deploy
Bundle用ボリューム作成
最後にdocker-compose.ymlを修正する。
- 毎度
bundle install
が必要になるのでインストール先専用のボリュームを作成 - Jets起動コマンドを追加
version: '3' services: app: build: context: . dockerfile: ./docker/ruby/Dockerfile volumes: - .:/app - bundle-data:/usr/local/bundle ports: - "8888:8888" command: bundle exec jets server --port 8888 --host 0.0.0.0 volumes: bundle-data: driver: local
起動
Dockerを起動する。
docker-compose up
以下の画面が表示される。