在 Rails 数据库中存储和稍后访问 JSON 对象的最佳实践

Posted

技术标签:

【中文标题】在 Rails 数据库中存储和稍后访问 JSON 对象的最佳实践【英文标题】:Best practice for storing and later accessing JSON object in rails database 【发布时间】:2021-09-21 14:13:13 【问题描述】:

我正在创建一个可以上传、分析和标记照片的 Ruby-on-rails。我们正在使用Azure's Cognitive Services,例如返回与给定图像关联的标签列表。

一个 JSON 格式的图像的标签示例是:

"tags":[
"tag_name":"outdoor","tag_confidence":"99.87",
"tag_name":"clothing","tag_confidence":"99.56",
"tag_name":"person","tag_confidence":"99.55",
"tag_name":"human face","tag_confidence":"93.33",
"tag_name":"tree","tag_confidence":"93.17",
"tag_name":"smile","tag_confidence":"91.33"

现在我们只是将它作为哈希存储在表中,但稍后我们希望能够进行过滤或查询,例如,我们可以在其中进行过滤或查询。获取所有带有 person 标记的图像。如何以最佳方式为此创建模型和表格?

【问题讨论】:

Postgres 有一个 JSON 数据类型 postgresql.org/docs/current/datatype-json.html @Eyeslandic 啊,是的,谢谢!这就是我们正在使用的。以后在大量图片上查询所有带有特定标签的图片可以吗? 通过适当的索引,我想是的,但我不是数据库专家。你读过关于 GIN 索引的部分吗,那里有关于性能的讨论。 我不确定 GIN 索引对 JSONB 列中的对象(文档)数组的使用效果如何。但我也不是数据库专家。 【参考方案1】:

如果您计划查询数据并且它具有常规结构,那么使用实际表可能会更好。

# rails g model tag name:string:uniq
class Tag < ApplicationRecord
  validate_uniqueness_of :name
  has_many :taggings
  has_many :photos, through: :taggings
end

# rails g model tagging tag:belongs_to photo:belongs_to confidence:float
# add a unique index on tag_id and photo_id
class Tagging < ApplicationRecord
  validate_uniqueness_of :tag_id, scope: :photo_id
  belongs_to :tag
  belongs_to :photo
end

class Photo < ApplicationRecord
  has_many :taggings
  has_many :tags, through: :taggings
end
class TaggingService
  def initialize(photo)
    @photo = photo
    @tag_data = get_it_somehow(photo)
  end

  def perform(photo)
    @tag_data.each do |raw|
      photo.taggings.create(
        confidence: raw["tag_confidence"],
        tag: Tag.find_or_create_by(name: raw["tag_name"])
      )
    end
  end

  def self.perform(photo)
    new(photo).perform
  end
end

例如,您可以通过以下方式查询带有给定标签的照片:

Photo.joins(:tags)
     .where(tags:  name: 'outdoor' )

或者一组标签:

Photo.left_joins(:tags)
     .group(:id)
     .where(tags:  name: ['outdoor', 'clothing', 'tree'] )
     .having('COUNT(tags.*) >= 3')

当然,您可以使用 JSON 列执行此操作,但查询会更难阅读,并且您会失去数据库规范化和使用实际模型的所有优势。

一个例子是,如果您想基于聚合显示标签:

Tag.left_joins(:taggings)
   .order('COUNT(taggings.*)', 'AVG(taggings.confidence)')

如果您使用 JSONB 列,则必须使用噩梦般的查询从 photos 表的每一行中提取该列。

【讨论】:

以上是关于在 Rails 数据库中存储和稍后访问 JSON 对象的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

使用 Rails 5 在 Postgres 中存储字符串化 JSON

使用 Rails 5.2 ActiveStorage 创建和保存 pdf 并稍后附加到电子邮件

Rails 从 JSON 数据中访问散列中的键

ExtJs4 json data.store 和 Rails

将 json 文件保存到内部存储器,然后稍后调用该文件

ARM - 如何从存储帐户获取访问密钥,以便稍后在模板中的 AppSettings 中使用?