在 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 并稍后附加到电子邮件