Самый быстрый способ заказа путем сопоставления имеет много через ассоциацию? - PullRequest
1 голос
/ 27 января 2012

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

Например:

  • У продукта может быть много тегов через ProductTags
  • Когда пользователь выбирает теги, я хотел бы упорядочить продукты по количествуВыбранные теги есть у каждого товара.

Можно ли использовать cache_counter или что-то подобное в этом случае?Я не уверен, что использование sort - лучший вариант.Правильно ли я считаю, что использование order в реальной базе данных, как правило, быстрее, чем sort?

Разъяснение / обновление

Извините, если вышеприведенное сбивает с толку.В основном то, что я ищу, ближе к упорядоченности по релевантности.Например, пользователь может выбрать теги 1, 2 и 4. Если с продуктом связаны все теги дерева, я хочу, чтобы этот продукт был указан первым.Второй продукт может иметь только теги 1 и 4. И так далее.Я почти уверен, что для этого придется использовать sort против order, но мне было интересно, нашел ли кто-нибудь более эффективный способ сделать это.

Ответы [ 2 ]

0 голосов
/ 04 декабря 2012

Опираясь на отличный ответ Райана, я хотел метод, который мог бы использоваться acts-as-taggable-on и подобные плагины (таблицы с именем tags / taggings), и в итоге получил следующее:

def Product.find_by_tag_list(tag_list)
  tag_list_sql = "'" + tag_list.join("','") + "'"
  Product.find_by_sql("SELECT * FROM products, (SELECT taggable_id, COUNT(*) AS relevance FROM taggings LEFT JOIN tags ON tags.id = taggings.tag_id WHERE tags.name IN (" + tag_list_sql + ") GROUP BY taggable_id) AS r WHERE products.id = r.taggable_id ORDER BY r.relevance DESC;")
end

Чтобы получить список товаров, упорядоченных по релевантности, я могу сделать:

Product.find_by_tag_list(my_product.tag_list)
0 голосов
/ 27 января 2012

Упорядочение по релевантности в базе данных возможно и гораздо более эффективно, чем использование метода сортировки в Ruby. Предполагая следующую структуру модели и соответствующую базовую структуру таблицы SQL:

class Product < ActiveRecord::Base
  has_many :product_taggings
  has_many :product_tags, :through => :product_taggings
end

class ProductTags < ActiveRecord::Base
  has_many :product_taggings
  has_many :products, :through => :product_taggings
end

class ProductTaggings < ActiveRecord::Base
  belongs_to :product
  belongs_to :product_tags
end

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

SELECT
  `product_id`
  ,COUNT(*) AS relevance
FROM
  `product_taggings` AS ptj
LEFT JOIN
  `products` AS p
    ON p.`id` = ptj.`product_id`
LEFT JOIN
  `product_tags` AS pt
    ON pt.`id` = ptj.`product_tag_id`
WHERE
  pt.`name` IN ('Tag 1', 'Tag 2')
GROUP BY
  `product_id`

Если у меня есть следующие продукты и связанные теги:

Product 1 -> Tag 3
Product 2 -> Tag 1, Tag 2
Product 3 -> Tag 1, Tag 3

Тогда предложение WHERE сверху должно дать мне ответ:

product_id | relevance
----------------------
         2 |         2
         3 |         1

* Product 1 is not included since there were no matches.
  Given that the user is performing a filtered search,
  this behavior is probably fine.  There's a way to get
  Product 1 into the results with 0 relevance if
  necessary.

То, что вы сделали, - это создали небольшой набор результатов, который может выступать в качестве своего рода встроенной таблицы соединений. Чтобы прикрепить оценку релевантности к каждой строке запроса из таблицы products, используйте этот запрос как подзапрос следующим образом:

SELECT *
FROM
  `products` AS p
  ,(SELECT
      `product_id`
      ,COUNT(*) AS relevance
    FROM
      `product_taggings` AS ptj
    LEFT JOIN
      `products` AS p
        ON p.`id` = ptj.`product_id`
    LEFT JOIN
      `product_tags` AS pt
        ON pt.`id` = ptj.`product_tag_id`
    WHERE
      pt.`name` IN ('Tag 1', 'Tag 2')
    GROUP BY `product_id`
  ) AS r
WHERE
  p.`id` = r.`product_id`
ORDER BY
  r.`relevance` DESC

У вас будет набор результатов, содержащий поля из вашей таблицы products и дополнительный столбец релевантности в конце, который затем будет использоваться в предложении ORDER BY.

Вам нужно будет написать метод, который заполнит этот запрос желаемым списком pt.name IN. Обязательно очистите этот список , прежде чем подключать его к запросу, иначе вы откроете себя для возможного внедрения SQL.

Возьмите результат вашего метода сборки запроса и выполните его через Product.find_by_sql(my_relevance_sql), чтобы предварительно отсортировать ваши модели по релевантности непосредственно из БД.

Очевидным недостатком является то, что вы вводите специфичную для СУБД зависимость в свой код Rails (и рискуете ввести SQL, если не будете осторожны). Если вы не используете MySQL, возможно, потребуется изменить синтаксис. Однако он должен работать намного быстрее, особенно на огромном наборе результатов, чем использование Ruby sort для результатов. Кроме того, добавление предложения LIMIT даст вам поддержку пагинации при необходимости.

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