Rails + PostgreSQL 数组
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
如果你继续阅读本文,我假设你了解 Ruby、Ruby 中的面向对象编程 (OOP)、Ruby Ruby 框架 (RoR) 和 Active Record。
是的,PostgreSQL 支持存储数组类型。根据他们的文档:
PostgreSQL 允许将表的列定义为可变长度的多维数组。可以创建任何内置或用户定义的基本类型、枚举类型、复合类型、范围类型或域的数组。
让我们开始这段旅程吧!(我这里只使用 Rails API 作为示例,但本文内容同样适用于普通的 Rails 版本。)
目录:
1. 迁移
2. 创建
3. 显示
4. 更新
5. 查询
6. 结语
1. 迁移
很简单:
# db/migrate/*_create_books.rb
class CreateBooks < ActiveRecord::Migration[6.0]
def change
create_table :books do |t|
t.string :title
t.string :tags, array: true, default: []
t.integer :ratings, array: true, default: []
t.timestamps
end
add_index :books, :tags, using: 'gin'
add_index :books, :ratings, using: 'gin'
end
end
如果您想添加新列:
# db/migrate/*_add_subjects_to_books.rb
class AddSubjectsToBooks < ActiveRecord::Migration
def change
add_column :books, :subjects, :string, array:true, default: []
end
end
注意:
我t.string :tags, array: true并未定义该列t.array :tags。与 jsonb 不同,jsonb 定义了该列t.jsonb :payload。这是因为 PostgreSQL 中没有“数组”类型,只有“列类型数组”。PostgreSQL 数组并非像 Ruby 数组那样的通用容器,它们更像是 C、C++ 等语言中的数组。
2. 创建
创建记录也很简单:
irb(main):001:0> Book.create(title: "Hacking Growth", tags: ["business", "startup"], ratings: [4, 5])
(0.1ms) BEGIN
Book Create (0.6ms) INSERT INTO "books" ("title", "tags", "ratings", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["title", "Hacking Growth"], ["tags", "{business,startup}"], ["ratings", "{4,5}"], ["created_at", "2020-06-29 08:48:42.440895"], ["updated_at", "2020-06-29 08:48:42.440895"]]
(0.4ms) COMMIT
=> #<Book id: 1, title: "Hacking Growth", tags: ["business", "startup"], ratings: [4, 5], created_at: "2020-06-29 08:48:42", updated_at: "2020-06-29 08:48:42">
3. 显示
两者tags现在ratings都是一个数组对象:
irb(main):002:0> book = Book.first
Book Load (0.3ms) SELECT "books".* FROM "books" ORDER BY "books"."id" ASC LIMIT $1 [["LIMIT", 1]]
irb(main):003:0> book.tags
=> ["business", "startup"]
irb(main):004:0> book.tags[0]
=> "business"
4. 更新
更新的最简单方法是:
irb(main):005:0> book.tags << 'management'
=> ["business", "startup", "management"]
irb(main):0006:0> book.save!
(0.1ms) BEGIN
Book Update (1.2ms) UPDATE "books" SET "tags" = $1, "updated_at" = $2 WHERE "books"."id" = $3 [["tags", "{business,startup,management}"], ["updated_at", "2020-06-29 08:54:36.731328"], ["id", 1]]
(0.4ms) COMMIT
=> true
irb(main):007:0> book.tags
=> ["business", "startup", "management"]
还有其他任何向数组对象添加值的方法吗?
# This works
book.tags << 'management'
#This will work too
book.tags.push 'management'
# This is also will work
book.tags += ['management']
但不要这样做:Book.first.tags << 'finance',它不会保存到数据库中。证明:
irb(main):008:0> Book.first.tags << "finance"
Book Load (0.3ms) SELECT "books".* FROM "books" ORDER BY "books"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> ["business", "startup", "management", "finance"]
irb(main):009:0> Book.first.save!
Book Load (0.3ms) SELECT "books".* FROM "books" ORDER BY "books"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> true
irb(main):010:0> Book.first.tags
Book Load (0.3ms) SELECT "books".* FROM "books" ORDER BY "books"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> ["business", "startup", "management"]
如果要使用原始 SQL,可以查阅官方文档。
5. 查询
假设我们要搜索所有带有“管理”标签的书籍:
# This is valid
irb(main):011:0> Book.where("'management' = ANY (tags)")
# This is more secure
irb(main):012:0> Book.where(":tags = ANY (tags)", tags: 'management')
# This is also valid
irb(main):013:0> Book.where("tags @> ?", "{management}")
如果我们想搜索所有没有标签管理的书籍,该怎么办?
irb(main):013:0> Book.where.not("tags @> ?", "{management}")
您可以在官方文档中查看运算符及其说明。
那么,如果我们想搜索包含多个标签(例如管理和创业)的书籍该怎么办呢?
# This is valid
irb(main):014:0> Book.where("tags @> ARRAY[?]::varchar[]", ["management", "startup"])
# This is valid
irb(main):015:0> Book.where("tags && ?", "{management,startup}")
# If you use where.not, you basically search for all that do not contain the parameter given.
如果我们想搜索所有评分高于 3 分的书籍,该怎么办呢?
irb(main):016:0> Book.where("array_length(ratings, 1) >= 3")
我们不妨让搜索功能更强大一些,并支持模式匹配:
# %gem% is manaGEMent
irb(main):017:0> Book.where("array_to_string(tags, '||') LIKE :tags", tags: "%gem%")
您可以在官方文档中查看所有运算符和函数及其说明。
6. 结语
以上就是全部内容。如果我发现什么有趣的东西,我会更新。
来源:本人以及摘自多篇文章
文章来源:https://dev.to/kputra/rails-postgresql-array-1jn0