Добрый день!
Имею следующую проблему:
Есть довольно объемный вьюс, который выбирает ноды с кучей джоинов и фильтрацией по полям с множественным выбором.
Когда вьюс выводит просто все подряд то ноды идут правильно, без повторов, но стоит выставить одновременно фильтр и сортировку по популярности (на базе votingapi), как примерно на 3-4 странице начинаются повторы. Притом повторы идут какими-то кластерами, т.е. не просто в разнобой, а повторяются группы нод.
В настройках выставлен флаг уникальности, но это не дает эффект. Притом я пробовал разные комбинации, выставляя флаги для каждого поля фильтрации с множественным выбором, для всего вьюса в целом, ставил pure distinct флаг - но толку не было.
Пока я не наткнулся на этот патч - http://drupal.org/node/1717374. Он добавляет в запрос group by по уникальному полю, в моем случае nid.
И это решило проблему! Но только на локали!! 8( На продакшене как были повторы, так и остались, притом что devel показывает что group by в запрос добавляется.
Кеширование у вьюхи выключено, никаких пост-обработок запроса нет, да и в любом случае - на локали же блин работает!!
Подскажите куда копать, могут ли настройки mysql на сервере влиять на результаты?
Существует ли модуль для жесткой проверки на уникальность? Например она записывает какие nid'ы уже были в выдаче и исключает их через NOT IN.
Комментарии
БД не может влиять на запрос, его формирует Друпал. Distinct это вырожденный случай Group by так что добавляется группировка или нет разницы быть не должно.
Кидайте сюда запрос (с дев сайта и с продакшена)
Я в своем модуле делал hook_views_query_alter, в котором менял запрос для конкретного вьюза, который выводил дубляж.
Я вообще ничего не понимаю, devel отказывается показывать мне запрос на локали хоть ты тресни!
Вот запрос на продакшене:
SELECT DISTINCT node.nid AS nid, node.title AS node_title, field_data_field_rest_no_bookings.field_rest_no_bookings_value AS field_data_field_rest_no_bookings_field_rest_no_bookings_val, votingapi_cache_node_percent_rating_average.value AS votingapi_cache_node_percent_rating_average_value, field_data_field_rest_avg_bill.field_rest_avg_bill_value AS field_data_field_rest_avg_bill_field_rest_avg_bill_value, node.created AS node_created, 'node' AS field_data_field_rest_type_node_entity_type, 'node' AS field_data_field_rest_photo_node_entity_type, 'node' AS field_data_field_rest_city_node_entity_type, 'node' AS field_data_field_rest_address_node_entity_type, 'node' AS field_data_field_rest_rating_node_entity_type, 'node' AS field_data_field_rest_avg_bill_node_entity_type, 'node' AS field_data_field_rest_metro_node_entity_type, 'node' AS field_data_field_rest_discount_node_entity_type, 'node' AS field_data_field_rest_address_map_node_entity_type, 'node' AS field_data_field_rest_cuisine_node_entity_type, 'node' AS field_data_field_rest_options_node_entity_type FROM node node LEFT JOIN field_data_field_rest_booking field_data_field_rest_booking ON node.nid = field_data_field_rest_booking.entity_id AND (field_data_field_rest_booking.entity_type = :views_join_condition_0 AND field_data_field_rest_booking.deleted = :views_join_condition_1) INNER JOIN commerce_product commerce_product_field_data_field_rest_booking ON field_data_field_rest_booking.field_rest_booking_product_id = commerce_product_field_data_field_rest_booking.product_id LEFT JOIN votingapi_cache votingapi_cache_node_percent_rating_average ON node.nid = votingapi_cache_node_percent_rating_average.entity_id AND (votingapi_cache_node_percent_rating_average.entity_type = :views_join_condition_2 AND votingapi_cache_node_percent_rating_average.value_type = :views_join_condition_3 AND votingapi_cache_node_percent_rating_average.tag = :views_join_condition_4 AND votingapi_cache_node_percent_rating_average.function = :views_join_condition_5) LEFT JOIN field_data_commerce_stock commerce_product_field_data_field_rest_booking__field_data_commerce_stock ON commerce_product_field_data_field_rest_booking.product_id = commerce_product_field_data_field_rest_booking__field_data_commerce_stock.entity_id AND (commerce_product_field_data_field_rest_booking__field_data_commerce_stock.entity_type = :views_join_condition_6 AND commerce_product_field_data_field_rest_booking__field_data_commerce_stock.deleted = :views_join_condition_7) LEFT JOIN field_data_field_booking_date commerce_product_field_data_field_rest_booking__field_data_field_booking_date ON commerce_product_field_data_field_rest_booking.product_id = commerce_product_field_data_field_rest_booking__field_data_field_booking_date.entity_id AND (commerce_product_field_data_field_rest_booking__field_data_field_booking_date.entity_type = :views_join_condition_8 AND commerce_product_field_data_field_rest_booking__field_data_field_booking_date.deleted = :views_join_condition_9) LEFT JOIN field_data_field_rest_avg_bill field_data_field_rest_avg_bill ON node.nid = field_data_field_rest_avg_bill.entity_id AND (field_data_field_rest_avg_bill.entity_type = :views_join_condition_10 AND field_data_field_rest_avg_bill.deleted = :views_join_condition_11) LEFT JOIN field_data_field_rest_no_bookings field_data_field_rest_no_bookings ON node.nid = field_data_field_rest_no_bookings.entity_id AND (field_data_field_rest_no_bookings.entity_type = :views_join_condition_12 AND field_data_field_rest_no_bookings.deleted = :views_join_condition_13) WHERE (( (node.status = :db_condition_placeholder_14) AND (node.type IN (:db_condition_placeholder_15)) AND (commerce_product_field_data_field_rest_booking__field_data_commerce_stock.commerce_stock_value >= :db_condition_placeholder_16) AND (DATE_FORMAT(commerce_product_field_data_field_rest_booking__field_data_field_booking_date.field_booking_date_value, '%Y-%m-%d') = :field_data_field_booking_date_field_booking_date_value) AND (field_data_field_rest_avg_bill.field_rest_avg_bill_value BETWEEN :db_condition_placeholder_17 AND :db_condition_placeholder_18) )) GROUP BY nid ORDER BY field_data_field_rest_no_bookings_field_rest_no_bookings_val ASC, votingapi_cache_node_percent_rating_average_value DESC LIMIT 10 OFFSET 0
И что он возвращает? выполните в phpmyadmin
Ситуация становится все забавнее.
PMA выдает на тех же запросах другие результаты... ))
Вот запрос:
FROM
node node
LEFT JOIN field_data_field_rest_booking field_data_field_rest_booking ON node.nid = field_data_field_rest_booking.entity_id AND (field_data_field_rest_booking.entity_type = 'node' AND field_data_field_rest_booking.deleted = '0')
INNER JOIN commerce_product commerce_product_field_data_field_rest_booking ON field_data_field_rest_booking.field_rest_booking_product_id = commerce_product_field_data_field_rest_booking.product_id
LEFT JOIN votingapi_cache votingapi_cache_node_percent_rating_average ON node.nid = votingapi_cache_node_percent_rating_average.entity_id AND (votingapi_cache_node_percent_rating_average.entity_type = 'node' AND votingapi_cache_node_percent_rating_average.value_type = 'percent' AND votingapi_cache_node_percent_rating_average.tag = 'rating' AND votingapi_cache_node_percent_rating_average.function = 'average')
LEFT JOIN field_data_field_booking_date commerce_product_field_data_field_rest_booking__field_data_field_booking_date ON commerce_product_field_data_field_rest_booking.product_id = commerce_product_field_data_field_rest_booking__field_data_field_booking_date.entity_id AND (commerce_product_field_data_field_rest_booking__field_data_field_booking_date.entity_type = 'commerce_product' AND commerce_product_field_data_field_rest_booking__field_data_field_booking_date.deleted = '0')
LEFT JOIN field_data_field_rest_avg_bill field_data_field_rest_avg_bill ON node.nid = field_data_field_rest_avg_bill.entity_id AND (field_data_field_rest_avg_bill.entity_type = 'node' AND field_data_field_rest_avg_bill.deleted = '0')
LEFT JOIN field_data_field_rest_no_bookings field_data_field_rest_no_bookings ON node.nid = field_data_field_rest_no_bookings.entity_id AND (field_data_field_rest_no_bookings.entity_type = 'node' AND field_data_field_rest_no_bookings.deleted = '0')
WHERE (( (node.status = '1') AND (node.type IN ('restaurant')) AND (DATE_FORMAT(commerce_product_field_data_field_rest_booking__field_data_field_booking_date.field_booking_date_value, '%Y-%m-%d') = '2013-02-21') AND (field_data_field_rest_avg_bill.field_rest_avg_bill_value BETWEEN '0' AND '5000') ))
GROUP BY nid
ORDER BY field_data_field_rest_no_bookings_field_rest_no_bookings_val ASC, votingapi_cache_node_percent_rating_average_value DESC
LIMIT 10 OFFSET 20
Результаты в PMA:
«19.05»
«Ar`Di» в Бутово
«У Гоголя»
«Брайтон»
«Нагасаки»
«Ar`Di» в Борисово
«Золотая вилка»
«CIPOLLINO»
«Арка»
«Le Grand Cafe»
Результаты во Views:
Ресторан «Кино»
Ресторан «Шантиль»
Ресторан «Рисири»
Ресторан «Чайхана и Villa Gusto»
Ресторан «Baan Thai»
Кафе «Сулико»
Ресторан «Бейрут»
Ресторан «Азия Микс»
Кафе «Let’s Dance»
Ресторан «Вилла Густо»
Запрос и там и там одинаковый, единственная разница, что по views запрос выдается после обработки, т.е. к нему пристыковываются еще пачка запросов на получение данных.
Но это же по идее не должно влиять на изначальную выдачу?
В общем такое ощущение что на этапе формирования запроса какой-то модуль вносит в него свои правки, но тогда непонятно почему devel показывает такой же запрос?
При этом повторов нет только в одном случае, если поставить сортировку названию, при сортировке по средней оценке, или среднему чеку (повторяющиеся значения) сразу начинаются повторы.
В клинических случаях бывает что первая и вторая страницы выдачи 100% идентичны.
Выключил перезапись SQL в views, стало лучше, повторы все равно остались, но буквально 1-2 на 10 страниц, что в целом считаю приемлемым. На скорость и качество выдачи это не повлияло. Но почему так вообще происходит я все равно не понимаю 8(