Предел декартовых произведений Neo4j - оптимизация запросов - PullRequest
0 голосов
/ 08 ноября 2018

Учитывая следующую схему

enter image description here

Мне нужно получить список Collection каждый с набором ProductCard (это Product вариант) критериев соответствия, указанных пользователем:

  • тип коллекции
  • 1..4 типов продукта (Для каждого выбранного ProductType должен быть один ProductCard в наборе.)
  • цена за комплект

Я начал с запроса, подобного этому

MATCH (c:Collection {type: 'selected_collection_type'})<-[:FROM_COLLECTION]-(:Product)-[:OF_TYPE]->(pt1:ProductType {title: '1st product type'}), (c)<-[:FROM_COLLECTION]-(:Product)-[:OF_TYPE]->(pt2:ProductType {title: '2nd product type'}),(c)<-[:FROM_COLLECTION]-(:Product)-[:OF_TYPE]->(pt3:ProductType {title: '3rd product type'}), (c)<-[:FROM_COLLECTION]-(:Product)-[:OF_TYPE]->(pt4:ProductType {title: '4th product type'})
CALL apoc.cypher.run('
WITH {c} AS c, {pt1} AS pt1, {pt2} AS pt2, {pt3} AS pt3, {pt4} AS pt4
    MATCH (pt1)<-[:OF_TYPE]-(p1:Product)-[:FROM_COLLECTION]->(c), (pt2)<-[:OF_TYPE]-(p2:Product)-[:FROM_COLLECTION]->(c), (pt3)<-[:OF_TYPE]-(p3:Product)-[:FROM_COLLECTION]->(c), (pt4)<-[:OF_TYPE]-(p4:Product)-[:FROM_COLLECTION]->(c), (pc1:ProductCard)-[:VARIANT_OF]->(p1), (pc2:ProductCard)-[:VARIANT_OF]->(p2), (pc3:ProductCard)-[:VARIANT_OF]->(p3), (pc4:ProductCard)-[:VARIANT_OF]->(p4)
    WHERE (pc1.price + pc2.price + pc3.price + pc4.price < price_margin_for_set)
    RETURN pc1, pc2, pc3, pc4, (p1.weight + p2.weight + p3.weight + p4.weight) AS sweight ORDER BY sweight DESC LIMIT 1
', {c:c, pt1:pt1, pt2:pt2, pt3:pt3, pt4:pt4}) YIELD value
RETURN c, value ORDER BY value.sweight DESC LIMIT 8;

и он работает достаточно хорошо для 3 выбранных типов продуктов, но когда я добавляю 4-й тип продукта, все резко замедляется. Проблема здесь в том, что мне просто нужен 1 набор, возвращенный из подзапроса, но декартово произведение, вычисленное для всех вариантов продукта (Product может иметь 1 .. ~ 10 ProductCard), достаточно велико для 4 типов.

Как оптимизировать этот запрос для повышения производительности / уменьшить количество вариаций, необходимых для возврата 1 набора критериев соответствия цены из подзапроса?

Вот ОБЪЯСНИТЬ 1033 * объяснить *

EDIT: немного изменил запрос

WITH ['Product Type 1', 'Product Type 2', 'Product Type 3', 'Product Type 4'] as types
MATCH (c:Collection)<-[:FROM_COLLECTION]-(:Product)-[:OF_TYPE]->(pt:ProductType)
WHERE pt.title in types AND c.type = 'collection type'
WITH c, size(types) as inputCnt, count(DISTINCT pt) as cnt
WHERE cnt = inputCnt
CALL apoc.cypher.run('
WITH {c} AS c 
MATCH (c)<-[:FROM_COLLECTION]-(p1:Product)-[:OF_TYPE]->(:ProductType {title: "Product Type 1"})
MATCH (pc1:ProductCard)-[:VARIANT_OF]->(p1)
MATCH (c)<-[:FROM_COLLECTION]-(p2:Product)-[:OF_TYPE]->(:ProductType {title: "Product Type 2"})
MATCH (pc2:ProductCard)-[:VARIANT_OF]->(p2)
MATCH (c)<-[:FROM_COLLECTION]-(p3:Product)-[:OF_TYPE]->(:ProductType {title: "Product Type 3"})
MATCH (pc3:ProductCard)-[:VARIANT_OF]->(p3)
MATCH (c)<-[:FROM_COLLECTION]-(p4:Product)-[:OF_TYPE]->(:ProductType {title: "Product Type 4"})
MATCH (pc4:ProductCard)-[:VARIANT_OF]->(p4)
WHERE (pc1.price + pc2.price + pc3.price + pc4.price < 1000)
RETURN pc1, pc2, pc3, pc4, (p1.weight + p2.weight + p3.weight + p4.weight) AS sweight ORDER BY sweight DESC LIMIT 1
', {c:c}) YIELD value
RETURN DISTINCT c, value LIMIT 8;

Объясните

whole query

Объяснить подзапрос

subquery

1 Ответ

0 голосов
/ 13 ноября 2018

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

WHERE p1.value > p2.value > p3.value > p4.value

Однако характер этого запроса по-прежнему приводит к множеству наборов с незначительными незначительными изменениями.Вместо этого я просто вернул бы WHERE p1.value < 1000 WITH COLLECT(p1) as possible и обработал бы остальную часть клиента, чтобы минимизировать передачу данных.(Если вы действительно хотите, вы можете упорядочить p1 по значению при сборе, а затем сложить наименьшее 3, а затем отфильтровать любое со значением более 1000-smallest_set, но это немного излишне)

...