Лучший способ объединить столбцы first_name и last_name в Ruby on Rails 3? - PullRequest
2 голосов
/ 25 февраля 2012

У меня есть метод в моей User модели:

def self.search(search)
  where('last_name LIKE ?', "%#{search}%")
end

Тем не менее, было бы неплохо, чтобы мои пользователи могли выполнять поиск по имени и фамилии в одном запросе.

Я думал создать virtual attribute так:

def full_name
  [first_name, last_name].join(' ')
end

Но эффективно ли это на уровне базы данных. Или есть более быстрый способ получения результатов поиска?

Спасибо за любую помощь.

Ответы [ 4 ]

1 голос
/ 25 февраля 2012

Виртуальный атрибут из вашего примера является просто методом класса и не может использоваться подобными методам ActiveRecord для запроса базы данных.

Самый простой способ получить результат поиска - это изменить Поиск метод:

def self.search(search)
  q = "%#{query}%"
  where("first_name + ' ' + last_name LIKE ? OR last_name + ' ' + first_name LIKE ?", [q, q])
end

, где синтаксис конкатенации varchar совместим с выбранной вами базой данных (в моем примере MS SQL).

0 голосов
/ 27 февраля 2012

Можно сделать область для обработки сложных режимов, вот пример из одного проекта, над которым я работаю:

   scope :search_by_name, lambda { |q|
      if q
        case q
        when  /^(.+),\s*(.*?)$/
         where(["(last_name LIKE ? or maiden_name LIKE ?) AND (first_name LIKE ? OR common_name LIKE ? OR middle_name LIKE ?)",
           "%#{$1}%","%#{$1}%","%#{$2}%","%#{$2}%","%#{$2}%"
           ])
        when /^(.+)\s+(.*?)$/
          where(["(last_name LIKE ? or maiden_name LIKE ?) AND (first_name LIKE ? OR common_name LIKE ? OR middle_name LIKE ?)",
            "%#{$2}%","%#{$2}%","%#{$1}%","%#{$1}%","%#{$1}%"
            ])
        else
           where(["(last_name LIKE ? or maiden_name LIKE ? OR first_name LIKE ? OR common_name LIKE ? OR middle_name LIKE ?)",
             "%#{q}%","%#{q}%","%#{q}%","%#{q}%","%#{q}%"
             ])
        end
      else
       {}
      end
}

Как вы можете видеть, я выполняю сопоставление регулярных выражений, чтобы обнаружить разные шаблоны и построить различные поиски в зависимости от того, что предоставляется. В качестве дополнительного бонуса, если ничего не предоставлено, он возвращает пустой хеш, который фактически равен where(true), и возвращает все результаты.

Как упоминалось в другом месте, БД не может индексировать столбцы, когда с обеих сторон используется подстановочный знак, такой как %foo%, поэтому это может привести к замедлению на очень больших наборах данных.

0 голосов
/ 25 февраля 2012

Одним из способов реализации этого является токенизация (разбиение) поискового запроса и создание условия для каждого токена:

  def self.search(query)
    conds  = []
    params = {}
    query.split.each_with_index do |token, index|
      conds.push "first_name LIKE :t#{index} OR last_name LIKE :t#{index}"
      params[:"t#{index}"] = "%#{token}%"
    end

    where(conds.join(" OR "), params)
  end

Также убедитесь, что вы предотвращаете атаки с использованием SQL-инъекций.

Однако для обработки поиска лучше использовать инструменты полнотекстового поиска, такие как ElasticSearch и его Ruby gem под названием Tire.

РЕДАКТИРОВАТЬ: Исправлен код.

0 голосов
/ 25 февраля 2012

Функциональность поиска в вашем примере все еще будет работать на уровне SQL.

Итак, следуя вашему примеру, ваш код поиска может быть:

def self.search_full_name(query)
  q = "%#{query}%"
  where('last_name LIKE ? OR first_name LIKE ?', [q, q])
end

ПРИМЕЧАНИЕ.- эти типы запросов LIKE, так как они имеют подстановочный знак в префиксе, будут медленными для больших наборов данных, даже если они проиндексированы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...