【问题标题】:How can I query Rails ActiveRecord data stored in arrays如何查询存储在数组中的 Rails ActiveRecord 数据
【发布时间】:2015-11-29 23:39:58
【问题描述】:

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

回顾一下:

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

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

我也尝试了以下方法。

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

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

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

【问题讨论】:

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


【解决方案1】:

你试过MentorData.where("'apple' = ANY (os_usage)")吗?

【讨论】:

    【解决方案2】:

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

    所以,试试

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

    看看是否可行。

    这是一个通配符搜索,但省略 % 的操作类似于 =

    看到这个问题:SQL LIKE with no wildcards the same as '='?

    这假设 os_usage 是一个序列化数组,其中支持该数据的列是一个字符串,并且在实例化您的 MentorData 时,rails 会反序列化

    编辑:我会找出你的数据库是如何存储数组的,所以也许你可以这样做

    "%'apple'%"
    

    以确保它不会选择名称中仅包含 apple 的 oses。

    【讨论】:

    • 我不得不做出改变,但% 起作用了。 MentorData.where('os_usage LIKE ?', "%apple%")
    • 另一个注意事项:ActiveRecord 实际上使数组成为一个字符串,这就是它起作用的原因
    • @mu 太短了,抱歉。我假设 os_usage 是一个序列化列,存储为一个字符串,然后反序列化回一个数组。 brianllamar,很高兴它起作用了:-)
    • 这不是一个完美的答案。举例来说,假设一个名为“IN OS”的新操作系统发布并在您的数据库中命名为in。然后,当您搜索包含in 的记录时,您将编写查询MentorData.where('os_usage LIKE "%in%"') 并获取所有包含linuxwindows 的记录,尽管它们可能不包含in,如“IN OS”中那样。
    【解决方案3】:

    也许您应该将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']
    

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

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

    同时查询所有 MentorData 记录以获取 OsUsage:

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

    我希望你觉得它有用:)

    【讨论】:

    • 这是一篇很棒的文章,很有意义。这似乎比我目前的方法更好。
    【解决方案4】:

    下面是当前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")
    

    【讨论】:

    • 如果我想检查值是空白数组怎么办?喜欢[]
    猜你喜欢
    • 2015-01-25
    • 1970-01-01
    • 1970-01-01
    • 2015-01-19
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 2015-08-08
    • 2011-06-02
    相关资源
    最近更新 更多