💭

ruby × OpenSearch

2022/03/21に公開

つくりたかったもの

AWSで動かしてるRailsアプリケーションから OpenSearch Serviceを使用したかった。
そのためのリクエスト用のモデルを作りたかった。

また、このはじめに。さんの記事でもあるように rubyの公式クライアントはまだない。 (2022/03/23時点)
https://zenn.dev/hajimeni/articles/682e81fa68c7af#awsの発表とクライアントライブラリの現状

OpenSearch client compatibility
https://opensearch.org/docs/latest/clients/index/

とはいえ、AWSの公式ドキュメントにrubyのコード例(↓)もあるのだけど、なんかゴチャゴチャしてる気がした。
なのでrubyを用いてスッキリしたコードで使いやすいコードにしたかった。
https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/request-signing.html#request-signing-ruby

ちなみに、AWS SDK for Ruby と標準 Ruby ライブラリを使用にしている。elasticsearchのgemは使わない。
理由は下記。

  • 理由は喧嘩別れしてOpenSearchができているので、今後のサポートが期待できないため
  • できるだけ外部への依存度を低くして、シンプル & どのAPIにも適用できるようにしたい。

ちなみに有志がelasticのgemをforkしてruby用のクライアントgemをつくってくれてるようです。
https://github.com/opensearch-project/opensearch-ruby

つくったもの

class OpenSearch
  require 'aws-sdk-opensearchservice'

  def self.request(http_method, url, body)
    uri = URI(url)

    case http_method
    when 'GET' then
      request = Net::HTTP::Get.new(uri)
    when 'POST' then
      request = Net::HTTP::Post.new(uri)
    when 'PUT' then
      request = Net::HTTP::Put.new(uri)
    when 'DELETE' then
      request = Net::HTTP::Delete.new(uri)
    else
      request = Net::HTTP::Post.new(uri)
    end

    # 署名
    signature = Aws::Sigv4::Signer.new(
      service: 'es',
      region: 'ap-northeast-1',
      access_key_id: ENV['AWS_ACCESS_KEY_ID'],
      secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
      # session_token: ENV['AWS_SESSION_TOKEN']
    ).sign_request(
      http_method: http_method,
      url: url,
      body: body
    )

    # リクエストの設定
    request.body = body
    request['Host'] = signature.headers['host']
    request['X-Amz-Date'] = signature.headers['x-amz-date']
    request['X-Amz-Security-Token'] = signature.headers['x-amz-security-token']
    request['X-Amz-Content-Sha256']= signature.headers['x-amz-content-sha256']
    request['Authorization'] = signature.headers['authorization']
    request['Content-Type'] = 'application/json'

    begin
      Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
        http.request(request)
      end
    rescue => e
      Rails.logger.error(e.message)
    end
  end

60行に満たないシンプルなもんだけど、これでリクエストにこまったことはない。

つかいかた

公式ドキュメントの例から。
https://opensearch.org/docs/1.0/opensearch/rest-api/document-apis/index-document/#example

url = "https://#{ENV['OPENSEARCH_HOST']}/sample-index/_doc/1"
document = { description: "To be or not to be, that is the question." }
response = OpenSearch.request('PUT', url, document.to_json)

シンプルがいいね。

Discussion