您可以根据 Rails 中的 JSONB 列建立关联吗?

Can you have an association based on a JSONB column in Rails?

我有一个 Rails 应用程序(rails v6.0.3,ruby 2.7.1)正在使用 Noticed gem 发送通知。我有以下模型配置:

class Vendor < ApplicationRecord
    has_noticed_notifications
end

has_noticed_notifications,如其 README 中所述,是“用于关联和销毁通知记录的助手 where(params: {param_name.to_sym => self})

所以当我像这样创建通知时...

        VendorAddedNotification.with(
          vendor: vendor,
          data_source: "user",
        ).deliver(some_user) # => Notification inserted!

我希望能够使用 Noticed 方法找到引用供应商的通知,如下所示:

vendor = Vendor.find ...
vendor.notifications_as_vendor # => Expected: [ Notification#123 ]

但是,输入始终是空数组 (Actual => [])

我查看了 their source code,看起来 notifications_as_vendor 是以下查询:

Notification.where(params: { :vendor => self }) # where self = an instance of the Vendor model

但是,这似乎不起作用,我不确定它是否应该这样做。我尝试了 运行 一个更简单的查询,看看它是否有效...

Notification.where(params: { :data_source => "user" })

但这也没有用。但是,当我 运行 使用不同签名的相同查询时,它确实:

Notification.where("params->>'data_source' = ?", "user")

所以我的问题是——这是 Notified 的错误,还是我的配置中遗漏了什么?我为此使用 PSQL,这是相关的架构:

...
  create_table "notifications", force: :cascade do |t|
    t.string "recipient_type", null: false
    t.bigint "recipient_id", null: false
    t.string "type", null: false
    t.jsonb "params"
    t.datetime "read_at"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["read_at"], name: "index_notifications_on_read_at"
    t.index ["recipient_type", "recipient_id"], name: "index_notifications_on_recipient_type_and_recipient_id"
  end

...

以下是相关模型:

class VendorAddedNotification < Noticed::Base
  deliver_by :database

  param :vendor
  param :data_source
end

class Notification < ApplicationRecord
  include Noticed::Model
  belongs_to :recipient, polymorphic: true
end

提前致谢!

我找到了它不起作用的原因,这似乎是 Notified 的问题。

简单来说SQL我运行:

# PLAIN SQL
select "params" from "notifications" limit 1 

哪个returns通知的参数(返回通知的id=77)

# PLAIN SQL Result
"{""added_by"": {""_aj_globalid"": ""gid://stack-shine/WorkspaceMember/269""}, ""data_source"": ""user"", ""_aj_symbol_keys"": [""workspace_vendor"", ""data_source"", ""added_by""], ""workspace_vendor"": {""_aj_globalid"": ""gid://stack-shine/WorkspaceVendor/296""}}"

现在 Rails 当我

vendor = Notification.find(77).params[:vendor]
vendor.notifications_as_vendor.to_sql

结果是...

"SELECT \"notifications\".* FROM \"notifications\" WHERE \"notifications\".\"params\" = '{\"vendor\":{\"_aj_globalid\":\"gid://stack-shine/Vendor/296\"},\"_aj_symbol_keys\":[\"vendor\"]}'"

...从该查询中提取的参数是:

'{\"vendor\":{\"_aj_globalid\":\"gid://stack-shine/Vendor/296\"},\"_aj_symbol_keys\":[\"vendor\"]}'

所以...在数据库中,序列化的参数是A,但是Rails是搜索B:

# A: `params` In the database
"{""added_by"": {""_aj_globalid"": ""gid://stack-shine/WorkspaceMember/269""}, ""data_source"": ""user"", ""_aj_symbol_keys"": [""vendor"", ""data_source"", ""added_by""], ""vendor"": {""_aj_globalid"": ""gid://stack-shine/Vendor/296""}}"
# B: `params` Searched with by Rails
"{\"vendor\":{\"_aj_globalid\":\"gid://stack-shine/Vendor/296\"},\"_aj_symbol_keys\":[\"vendor\"]}"

显然这个查询无法工作,因为数据库中的参数不是 Rails 正在搜索的参数。

数据库中的通知在“供应商”(“data_source”和“added_by”)之上有额外的参数,Vendor.这就是为什么 returns 什么都没有吗?

现在,我将通过将 vendor_id 存储在参数中并执行类似 Notification.where("params >> vendor_id = ?", 123)

的操作来简单地自己查找通知