使用活动记录搜索序列化数据

时间:2012-03-22 00:00:18

标签: ruby-on-rails search activerecord serialization

我正在尝试对序列化列进行简单查询,你是如何做到这一点的?

serialize :mycode, Array


1.9.3p125 :026 > MyModel.find(104).mycode
  MyModel Load (0.6ms)  SELECT `mymodels`.* FROM `mymodels` WHERE `mymodels`.`id` = 104 LIMIT 1
 => [43565, 43402] 
1.9.3p125 :027 > MyModel.find_all_by_mycode("[43402]")
  MyModel Load (0.7ms)  SELECT `mymodels`.* FROM `mymodels` WHERE `mymodels`.`mycode` = '[43402]'
 => [] 
1.9.3p125 :028 > MyModel.find_all_by_mycode(43402)
  MyModel Load (1.2ms)  SELECT `mymodels`.* FROM `mymodels` WHERE `mymodels`.`mycode` = 43402
 => [] 
1.9.3p125 :029 > MyModel.find_all_by_mycode([43565, 43402])
  MyModel Load (1.1ms)  SELECT `mymodels`.* FROM `mymodels` WHERE `mymodels`.`mycode` IN (43565, 43402)
 => [] 

9 个答案:

答案 0 :(得分:70)

这不是一个不减慢你的应用程序的技巧。您必须使用.to_yaml

确切的结果:

MyModel.where("mycode = ?", [43565, 43402].to_yaml)
#=> [#<MyModel id:...]

仅针对MySQL进行测试。

答案 1 :(得分:37)

基本上,你不能。 #serialize的缺点是你绕过了数据库的本机抽象。您几乎只能加载和保存数据。

也就是说,一种非常好的方法可以减慢您的应用程序爬行速度:

MyModel.all.select { |m| m.mycode.include? 43402 }

故事的道德:不要将#serialize用于您需要查询的任何数据。

答案 2 :(得分:31)

序列化数组以特定方式存储在数据库中,例如:

[1, 2, 3, 4]
in
1\n 2\n 3\n etc

因此查询将是

MyModel.where("mycode like ?", "% 2\n%")

%2之间留出空格。

答案 3 :(得分:17)

Noodl的答案是对的,但并不完全正确。

这实际上取决于您使用的数据库/ ORM适配器:例如,PostgreSQL现在可以存储和搜索哈希/ json - 请查看hstore。我记得读过PostgreSQl的ActiveRecord适配器现在正确处理它。如果您正在使用mongoid或类似的东西 - 那么您在任何地方都在数据库级别使用非结构化数据(即json)。

但是如果你使用的数据库无法真正处理哈希 - 比如MySQL / ActiveRecord组合 - 那么你使用序列化字段的唯一原因就是你可以在某些后台进程中创建/写入的某些数据并显示/按需输出 - 我在我的经验中发现的唯一两个用途是一些报告(比如产品型号上的统计字段 - 我需要存储产品的一些平均值和中位数),以及用户选项(例如他们的首选模板颜色) - 我真的不需要查询 - 但是用户信息 - 比如他们订阅邮件列表 - 需要可以搜索电子邮件爆炸。

  

PostgreSQL hstore ActiveRecord示例:

MyModel.where("mycode @> 'KEY=>\"#{VALUE}\"'")

<强>更新 截至2017年,MariaDB和MySQL都支持JSON字段类型。

答案 4 :(得分:8)

好消息!如果您将PostgreSQL与hstore一起使用(使用Rails 4非常简单),您现在可以完全搜索序列化数据。 This是一个方便的指南,here是PG的语法文档。

在我的例子中,我将一个字典存储为名为amenities的hstore列中的哈希。我想检查哈希值为1的几个查询设施,我只是做

House.where("amenities @> 'wifi => 1' AND amenities @> 'pool => 1'")

万岁以求改进!

答案 5 :(得分:7)

您可以使用sql LIKE语句查询序列化列。

 MyModel.where("mycode LIKE '%?%'", 43402)

这比使用include?更快,但是,您不能使用数组作为参数。

答案 6 :(得分:3)

There's a blog post from 2009 from FriendFeed,描述了如何在MySQL中使用序列化数据。

您可以做的是创建表,作为您要搜索的任何数据的索引。

创建包含可搜索值/字段

的模型

在您的示例中,模型看起来像这样:

class MyModel < ApplicationRecord
  # id, name, other fields...
  serialize :mycode, Array
end

class Item < ApplicationRecord
  # id, value...
  belongs_to :my_model
end

创建&#34;索引&#34;可搜索字段表

当您保存MyModel时,您可以执行以下操作来创建索引:

Item.where(my_model: self).destroy
self.mycode.each do |mycode_item|
  Item.create(my_model: self, value: mycode_item)
end

查询和搜索

然后当你想查询和搜索时,只需执行:

Item.where(value: [43565, 43402]).all.map(&:my_model)
Item.where(value: 43402).all.map(&:my_model)

您可以向MyModel添加一个方法,使其更简单:

def find_by_mycode(value_or_values)
  Item.where(value: value_or_values).all.map(&my_model)
end

MyModel.find_by_mycode([43565, 43402])
MyModel.find_by_mycode(43402)

为了加快速度,您需要为该表创建一个SQL索引。

答案 7 :(得分:0)

在这篇文章中使用以下评论

https://stackoverflow.com/a/14555151/936494

https://stackoverflow.com/a/15287674/936494

我能够成功查询模型中的序列化哈希

class Model < ApplicationRecord
  serialize :column_name, Hash
end

column_name

{ my_data: [ { data_type: 'MyType', data_id: 113 } ] }

我们可以通过以下方式查询

Model.where("column_name = ?", hash.to_yaml)

这将生成类似SQL查询

Model Load (0.3ms)  SELECT "models".* FROM "models" WHERE (column_name = '---
:my_data:
- :data_type: MyType
  :data_id: 113
')

如果任何人有兴趣在SQL终端中执行生成的查询,它都可以工作,但是应注意,该值应以存储在DB中的准确格式存储。但是,我在PostgreSQL newline character找到了另一种简单的方法来使用包含换行符的原始字符串

select * from table_name where column_name = E'---\n:my_data:\n- :data_type: MyType\n  :data_id: 113\n'

上述查询中最重要的部分是E

注意:我上面执行的数据库是PostgreSQL。

答案 8 :(得分:0)

要搜索序列化列表,您需要使用唯一字符对数据添加前缀和后缀。

示例:

而不是像这样:

2345,12345,1234567会导致您尝试搜索2345的问题,而是执行类似<2345>,<12345>,<1234567>并搜索<2345>的操作(搜索查询已转换)。当然,前缀/后缀字符的选择取决于将要存储的有效数据。如果希望|||被使用并且有可能<被使用,则可以改用|之类的东西。当然,这会增加现场使用的数据,并可能导致性能问题。

使用trigrams索引或类似的东西可以避免潜在的性能问题。

您可以像data.map { |d| "<#{d}>" }.join(',')一样序列化它,并通过data.gsub('<').gsub('>','').split(',')对其进行反序列化。序列化程序类可以很好地完成加载/提取数据的工作。

执行此操作的方法是将数据库字段设置为文本,并使用rails的serialize模型方法和自定义lib类。 lib类需要实现两种方法:

def self.dump(obj) # (returns string to be saved to database) def self.load(text) # (returns object)

带有持续时间的示例。从文章中提取出来,所以链接腐烂将无法获取,请访问文章以获取更多信息。该示例使用单个值,但是使用上述方法序列化值列表并反序列化列表非常简单。

class Duration
  # Used for `serialize` method in ActiveRecord
  class << self
    def load(duration)
      self.new(duration || 0)
    end

    def dump(obj)
      unless obj.is_a?(self)
        raise ::ActiveRecord::SerializationTypeMismatch,
          "Attribute was supposed to be a #{self}, but was a #{obj.class}. -- #{obj.inspect}"
      end

      obj.length
    end
  end


  attr_accessor :minutes, :seconds

  def initialize(duration)
    @minutes = duration / 60
    @seconds = duration % 60
  end

  def length
    (minutes.to_i * 60) + seconds.to_i
  end
end