如何查询存储在数组中的Rails ActiveRecord数据

时间:2015-09-04 00:31:25

标签: sql ruby-on-rails ruby postgresql activerecord

我有一个rails模型调用MentorData,它有一个名为os_usage的属性。 ose存储在类似['apple', 'linux']的数组中。

回顾一下:

$ MentorData.first.os_usage
=> ['apple',  'linux']

我希望能够查询包含apple的os_usage的所有MentorData的数据,但是当我搜索MentorData.where(os_usage: 'apple')时,我只会得到只能使用苹果而不是苹果的导师Linux操作系统。我需要以某种方式搜索,检查数组中是否包含apple。

我也尝试了以下内容。

MentorData.where('os_usage like ?', 'apple’)
MentorData.where('os_usage contains ?', 'apple’)
MentorData.where('os_usage contains @>ARRAY[?]', 'apple')

是否可以通过具有数组或项目的属性查询ActiveRecord中的数据?

数据库位于Postgres上,如果这有助于提供更原始的搜索查询。

4 个答案:

答案 0 :(得分:23)

以下是当前Rails Edge Guides中提供的示例:

# db/migrate/20140207133952_create_books.rb
create_table :books do |t|
  t.string 'title'
  t.string 'tags', array: true
  t.integer 'ratings', array: true
end
add_index :books, :tags, using: 'gin'
add_index :books, :ratings, using: 'gin'

# app/models/book.rb
class Book < ActiveRecord::Base
end

# Usage
Book.create title: "Brave New World",
            tags: ["fantasy", "fiction"],
            ratings: [4, 5]

## Books for a single tag
Book.where("'fantasy' = ANY (tags)")

## Books for multiple tags
Book.where("tags @> ARRAY[?]::varchar[]", ["fantasy", "fiction"])

## Books with 3 or more ratings
Book.where("array_length(ratings, 1) >= 3")

答案 1 :(得分:3)

您是否尝试过MentorData.where("'apple' = ANY (os_usage)")

答案 2 :(得分:1)

也许您应该从模型中分离os_usage数组并将其作为单独的表。

在ActiveRecord世界中,您将获得类似以下代码的内容:

class MentorData < ActiveRecord::Base
  ..
  has_and_belongs_to_many :os_usage
  ..
end

class OsUsage < ActiveRecord::Base
  ..
  has_and_belongs_to_many :mentors_data
  ..
end

在这两个模型之间创建many_to_many关系,可以让您轻松查询并避免重复。这种技术称为规范化

使用这个新的设计,你有 os_usage 的集合,而不是字符串

MentorData.first.os_usage
# => [#<OsUsage:....>, #<OsUsage:...>]

您可以轻松将其转换为旧的字符串数组

MentorData.first.os_usage.map(&:name)
# => ['apple',  'linux']

此外,您可以查询包含 apple 的os_usage的所有MentorData的数据:

MentorData.joins(:os_usages).where('os_usages.name' => 'apple')

并查询OsUsage的所有MentorData记录:

OsUsage.where(name: 'apple').mentors_data

我希望你觉得它很有用:)

答案 3 :(得分:0)

对于类似的查询,您需要%%来指示文字可以显示在搜索的左侧或右侧。

所以,试试

MentorData.where('os_usage LIKE "%apple%"')

看看是否有效。

这是一个外卡搜索,但省略%操作就像=

请参阅此问题:SQL LIKE with no wildcards the same as '='?

这假设os_usage是序列化数组,其中支持该数据的列是字符串,并且在实例化MentorData

时rails会反序列化

编辑:我发现你的数据库是如何存储数组的,所以也许你可以做到

"%'apple'%"

确保它不会选择名称中包含苹果的ose。