🚀
【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