Поиск сериализованных данных с использованием активной записи

Я пытаюсь сделать простой запрос сериализованного столбца, как вы это делаете?

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)
 => [] 

10 ответов

Решение

По сути, вы не можете. Недостатком #serialize является то, что вы игнорируете абстракции своей базы данных. Вы в значительной степени ограничены загрузкой и сохранением данных.

Тем не менее, один очень хороший способ замедлить ваше приложение для сканирования может быть:

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

Мораль истории: не используйте #serialize для каких-либо данных, к которым вам нужно обращаться.

Это просто хитрость, чтобы не замедлять работу вашего приложения. Вы должны использовать .to_yaml,

Точный результат:

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

Проверено только для MySQL.

Сериализованный массив хранится в базе данных определенным образом, например:

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

следовательно, запрос будет

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

поставить пространство между % а также 2,

Нудл ответ правильный, но не совсем правильный.

Это действительно зависит от используемого вами адаптера базы данных /ORM: например, PostgreSQL теперь может хранить и искать хэши /json - проверить hstore. Я помню, что читал, что адаптер ActiveRecord для PostgreSQl теперь обрабатывает его правильно. И если вы используете mongoid или что-то в этом роде - тогда вы используете неструктурированные данные (например, json) на уровне базы данных везде.

Однако, если вы используете базу данных, которая не может действительно обрабатывать хэши - например, комбинацию MySQL / ActiveRecord - тогда единственная причина, по которой вы бы использовали сериализованное поле, - это некоторые данные, которые вы можете создавать / записывать в некотором фоновом процессе и отображать / выводить по требованию - только два использования, которые я нашел в своем опыте, - это некоторые отчеты (например, поле статистики в модели продукта - где мне нужно хранить некоторые средние и медианы для продукта) и пользовательские параметры (например, предпочитаемый ими цвет шаблона - я действительно не нужно спрашивать об этом) - однако пользовательская информация - например, их подписка на список рассылки - должна быть доступна для поиска по электронной почте.

PostgreSQL hstore Пример ActiveRecord:

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

ОБНОВЛЕНИЕ Начиная с 2017 года MariaDB и MySQL поддерживают типы полей JSON.

Хорошие новости! Если вы используете PostgreSQL с hstore (что очень легко с Rails 4), теперь вы можете полностью искать сериализованные данные. Это удобное руководство, а вот синтаксическая документация от PG.

В моем случае у меня есть словарь, хранящийся в виде хэша в столбце hstore с именем amenities, Я хочу проверить пару запрашиваемых удобств, которые имеют значение 1 в хеше я просто делаю

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

Ура для улучшений!

Вы можете запросить сериализованный столбец с помощью оператора SQL LIKE.

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

Это быстрее, чем использование include?, Однако вы не можете использовать массив в качестве параметра.

В 2009 году от FriendFeed есть запись в блоге, в которой рассказывается, как использовать сериализованные данные в MySQL.

Что вы можете сделать, это создать таблицы, которые функционируют как индексы для любых данных, которые вы хотите найти.

Создайте модель, которая содержит доступные для поиска значения / поля

В вашем примере модели будут выглядеть примерно так:

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

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

Создание "индексной" таблицы для полей поиска

Когда вы сохраняете 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 для этой таблицы.

Используя следующие комментарии в этом сообщении

/questions/22596987/poisk-serializovannyih-dannyih-s-ispolzovaniem-aktivnoj-zapisi/22597002#22597002

/questions/22596987/poisk-serializovannyih-dannyih-s-ispolzovaniem-aktivnoj-zapisi/22596993#22596993

Мне удалось запросить сериализованный хэш в моей модели

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, он должен работать, однако следует позаботиться о том, чтобы значение было в точном формате, хранящемся в БД. Однако есть еще один простой способ, который я нашел в символе новой строки PostgreSQL, чтобы использовать необработанную строку, содержащую символы новой строки.

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

Самая важная часть в приведенном выше запросе - E.

Примечание. База данных, на которой я выполнил выше, - PostgreSQL.

Если у вас есть сериализованный столбец json, и вы хотите применить к нему подобный запрос. сделай так

      YourModel.where("hashcolumn like ?", "%#{search}%")

Для поиска в сериализованном списке вам нужно добавить к данным префиксы и постфиксы с уникальными символами.

Пример:

А не что-то вроде:

2345,12345,1234567 что может вызвать проблемы, которые вы пытались найти 2345 вместо этого вы делаете что-то вроде <2345>,<12345>,<1234567> и искать <2345>(поисковый запрос преобразуется). Конечно, выбор символов префикса / постфикса зависит от допустимых данных, которые будут храниться. Вместо этого вы можете использовать что-то вроде ||| если вы ожидаете < для использования и потенциально|быть использованным. Конечно, это увеличивает объем данных, используемых полем, и может вызвать проблемы с производительностью.

Использование индекса триграмм или чего-то еще позволит избежать потенциальных проблем с производительностью.

Вы можете сериализовать его как data.map { |d| "<#{d}>" }.join(',') и десериализуйте его через data.gsub('<').gsub('>','').split(','). Класс сериализатора достаточно хорошо справится с задачей загрузки / извлечения данных.

Это можно сделать, задав в поле базы данных текст и используя параметр rail serializemodel с настраиваемым классом библиотеки. Класс 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
Другие вопросы по тегам