🚀
【Rails】elasticsearch-railsのselfの使い方には気をつけて〜
発生した問題
- モデルファイル上でElasticsearchのクエリコードを書いていた際に、
selfの出力がおかしい - Elasticsearchの
importメソッドを呼び出した際の、クエリDSL内で用いたselfから出力された情報が想定していたものではなかった
-実装したいコード-
- Userのインスタンスに対して、そのUserに紐づいている商品のインデックスを更新する処理
-
selfを用いて、Userのインスタンスを指し示したい(self.exhibits.now_on_sale.ids)
# user.rb
def user_exhibits_no_recommendation
begin
Exhibit.__elasticsearch__.import(
query: -> {
# ココ
includes(:exhibit_images, :thinkings, :user, exhibit_sub_form_values: :exhibit_sub_form).where(id: self.exhibits.now_on_sale.ids) }
)
rescue StandardError
nil
end
end
考察
-
query -> { include(:exhibit_images, ...が呼べている時点で、selfがExhibitなのは妥当なのでは? - 具体的な原因は不明
原因(結果)
-
Exhibit.__elasticsearch__.importを呼び出すと、ClassMethodsProxyのインスタンスが生成され、このインスタンスがselfとして機能する -
queryブロック内のselfはExhibit.__elasticsearch__によって返されるClassMethodsProxyインスタンスを指すので、selfはUserのインスタンスではなく、Exhibitに関連するElasticsearchの操作を代理実行するプロキシオブジェクトを指す
確認したこと
-確認コード-
- 確認した点
- モデルファイル内で通常時に
selfが指し示すデータ - ElasticsearchのクエリDSL内で
selfが指し示すデータ
- モデルファイル内で通常時に
# user.rb
def user_exhibits_no_recommendation
begin
p '-----'
p self
p '-----'
Exhibit.__elasticsearch__.import(
query: -> {
p '----------------'
p self
p '----------------'
includes(:exhibit_images, :thinkings, :user, exhibit_sub_form_values: :exhibit_sub_form).where(id: "#{ここにUserのインスタンスを入れたい}".exhibits.now_on_sale.ids) }
)
rescue StandardError
nil
end
end
-出力ログ-
- 出力結果
- モデルファイル上で
selfを用いた場合、一般的にそのselfが指し示すのはModelのインスタンスであるが、importメソッドのクエリDSL内でselfを使用すると、指し示すデータが変わった-
#<User id: 1, ...>: Userのインスタンス -
[PROXY] Exhibit(id: integer, ...): Exhibitの情報(?)
-
- モデルファイル上で
"-----"
#<User id: 1, signin_count: 5, nickname: "ユーザー1", ..., updated_at: "2023-09-01 13:03:38.000000000 +0900">
"-----"
"----------------"
[PROXY] Exhibit(id: integer, game_id: integer, ..., updated_at: datetime)
"----------------"
解決策
- 先にUserのインスタンス情報を変数に格納する
def user_exhibits_no_recommendation
begin
# NOTE: selfのままでは更新されないため代入
user = self
Exhibit.__elasticsearch__.import(
query: -> { includes(:exhibit_images, :thinkings, :user, exhibit_sub_form_values: :exhibit_sub_form).where(id: user.exhibits.now_on_sale.ids) }
)
rescue StandardError
nil
end
end
結論
- Elasticsearchのブロック処理内で
selfが指し示すのは@__elasticsearc__ -
@__elasticsearch__は、@elasticsearch ||= ClassMethodsProxy.new(self)で初期化され、中身はClassMethodsProxyのインスタンスである -
ClassMethodsProxy.new(self)のselfは、メソッドの呼び出し対象であるExhibitクラスを指すので、ClassMethodsProxyのインスタンスはExhibitクラスのElasticsearch関連のメソッドを代理実行できるようになる
# コード
def self.__elasticsearch__ &block
@__elasticsearch__ ||= ClassMethodsProxy.new(self)
@__elasticsearch__.instance_eval(&block) if block_given?
@__elasticsearch__
end
※ 補足として、 self の出力の先頭に付いている [PROXY] は、プロキシオブジェクトであることを明示するために inspect メソッドがデバックやログ出力で表示する際に付けている
Discussion