Rails/Postgres 查询 Hstore 是否存在

Rails/Postgres query Hstore for presence

我的 Rails 应用程序中有一些 HStore 列。环顾 Posgresql 文档和 some blog posts,我发现,给定 HStore 值是这样的散列:

{ some_key: 'value' }

我可以像这样查询列:

Model.where("the_hstore_column -> 'some_key' = 'value'")

然而,作为一个足够的查询工具存在很多问题:

  1. 它真的只对超级简单的值有意义。如果值为 本身就是一个哈希值之类的,我不知道如何搜索它 有效,如果我不知道它的所有内容。 (即使我这样做了,我也会 必须将它们全部变成字符串化的 JSON 或其他东西)。

  2. 做这件事没有帮助(至少,我不能让它变得有帮助) 查询列内容是否存在(或 列下任意键的内容)。

换句话说(伪sql),我希望能够做到这一点:

Model.where("the_hstore_column = {}")
Model.where.not("the_hstore_column = {}")

或者:

Model.where("the_hstore_column -> 'some_key' = NULL")
Model.where.not("the_hstore_column -> 'some_key' = NULL")

或者最好的是,给定一个值为 { some_key: { sub_key: 'value' } }:

的 HStore
Model.where("the_hstore_column -> 'some_key' INCLUDES (?)", 'sub_key')
Model.where.not("the_hstore_column -> 'some_key' INCLUDES (?)", 'sub_key')

这些似乎不起作用,但就我的生活而言,我找不到有关如何进行这些查询的重要信息。有谁知道如何写它们,或者我在哪里可以找到更好的信息?

UPDATE 仔细查看后,我发现 this post,看起来很有希望,但我无法让代码实际工作。例如,Model.where("the_hstore_column ? :key", key: 'some_key') 返回一个空关系,即使在 the_hstore_column 中有许多 Model 对象具有 some_key

应 Andrius Buivydas 的要求,我将模型的相关部分粘贴在下方。它相对简短,因为我决定将 Hstore 访问抽象到我编写的一个模块中,本质上将它变成一个散列存储(虽然我显然不正确,但这是它的全部目的)。我尝试使用内置的 store_accessor,但它并不符合我的喜好,(似乎没有帮助解析保存的哈希值,因为现在显而易见的原因)因此下面的 ActiveRecord::MetaExt::HstoreAccessor 模块. (ParsedHstore 只是获取一个 HStore 字符串并将其解析为散列)。

class Place.rb

  include ActiveRecord::MetaExt::HstoreAccessor
  hstore_accessor :hours, :extra

end

(在单独的文件中):

module ActiveRecord
  module MetaExt
    module HstoreAccessor

      module ClassMethods

        def hstore_accessor(*symbols)
          symbols.each do |field|
            class_eval do 
              define_method(field) do
                ParsedHstore.new(self[field.to_sym]).hash_value
              end

              define_method("add_to_#{field}!") do |arg|
                self[field.to_sym] = self[field.to_sym].merge(arg)
                send("#{field}_will_change!")
                save
                arg
              end

              define_method("add_to_#{field}") do |arg|
                self[field.to_sym] = self[field.to_sym].merge(arg)
                send("#{field}_will_change!")
                arg
              end

              define_method("remove_from_#{field}") do |arg|
                other = self[field].dup
                other.delete(arg.to_s); other.delete(arg.to_sym)
                self[field.to_sym] = other
                send("#{field}_will_change!")
                self[field.to_sym]
              end

              define_method("remove_from_#{field}!") do |arg|
                other = self[field].dup
                other.delete(arg.to_s); other.delete(arg.to_sym)
                self[field.to_sym] = other
                send("#{field}_will_change!")
                save
                self[field.to_sym]
              end

              define_method("set_#{field}") do |arg|
                self[field.to_sym] = arg
                send("#{field}_will_change!")
                self[field.to_sym]
              end

              define_method("set_#{field}!") do |arg|
                self[field.to_sym] = arg
                send("#{field}_will_change!")
                self[field.to_sym]
              end
            end
          end
        end
      end

      def self.included(base)
        base.extend ClassMethods
      end
    end
  end
end

我的想法是,这让我可以轻松地将 add/remove 值赋给 HStore 字段,而不必每次都考虑合并和 _will_change! 逻辑。所以我可以这样做,例如:Place.first.add_to_extra({ key: 'value'}).

Should I have made these fields json or jsonb? I feel like I'm reinventing the wheel here, or, more aptly, trying to turn a horse into a wheel or something.

另外,我可能误解了查询示例。我从字面上在我的数据库中尝试了这个查询(它有很多地方在 extra 字段下有一个非空的 ranking 键),并且出现了一个空关系:

Place.where("extra ? :key", key: 'ranking')

I wouldn't be surprised if I messed this syntax up, as it seems really strange. Wouldn't that replace the ? with 'ranking', turning the query into this?: Place.where("extra ranking :key")? Seems weird and emphatically different from any other SQL I've run. Or is it turning to Place.where("extra ? ranking")? But ? is usually for safe injection of variables, no?

如果我的模型或其他地方的其他内容更相关,请告诉我。

It really only makes sense for super simple values. If the value is itself a hash or something, I have no idea how to search for it effectively, if I don't know all its contents.

Postgresql HStore 仅存储都是字符串的键值对。所以你不能存储散列、nil 或其他类似对象的东西——它们将被转换为字符串。

Model.where("the_hstore_column ? :key", key: 'some_key')

如果一切都正确定义,那应该可以工作。您能否粘贴带有 hstore 列值定义的模型文件的摘录?

另一种查找空 hstore 列的方法是

Model.where(hstore_column: ['',nil]) 

Model.where("hstore_column='' OR hstore_column IS NULL")