Neo4j схема денормализации
В моем приложении Neo4j у меня есть товары, характеристики товаров и список голосов (с удвоенным весом) для каждого товара по определенной характеристике.
Чтобы отсортировать список продуктов по среднему весу голосов для списка характеристик, мне нужно выполнить сложный запрос Cypher во время выполнения, который вычислит сумму среднего веса для списка характеристик для каждого продукта.
Это мой текущий запрос Cypher:
MATCH (parentP:Product)-[:CONTAINS]->(childP:Product)
WHERE id(parentP) = {productId}
WITH childP
OPTIONAL MATCH (p:Product)<-[:VOTED_FOR]-(v:Vote)-[:VOTED_ON]->(c:Characteristic)
WHERE id(p) = id(childP) AND id(c) IN {characteristicIds}
WITH childP, c, avg(v.weight) as weight
RETURN childP AS product, sum(weight) as weight
ORDER BY weight DESC
Я знаю, что Neo4j чрезвычайно быстр для обхода графа, но не очень хорош для агрегации (суммирование, подсчет, усреднение и т. Д.). Моя система может иметь большие списки голосов для каждой характеристики каждого продукта.
Пожалуйста, помогите мне денормировать эту структуру, чтобы избежать проблем с производительностью при большом количестве голосов.
ПРОФИЛЬ выход для запроса Брайана:
Версия Cypher: CYPHER 2.2, планировщик: СТОИМОСТЬ. 374933 дБ всего за 1482 мс.
3 ответа
Я не знаю, что я бы сказал, что Neo4j не подходит для агрегации. Вы могли бы помочь ему сэкономить некоторую работу, специально указав свой childP
в вашем втором матче:
MATCH (parentP)-[:CONTAINS]->(childP:Product)
WHERE id(parentP) = {productId}
OPTIONAL MATCH childP<-[:VOTED_FOR]-(v:Vote)-[:VOTED_ON]->(c)
WHERE id(c) IN {characteristicIds}
WITH childP, c, avg(v.weight) as weight
RETURN childP AS product, sum(weight) as weight
ORDER BY weight DESC
Я хочу убедиться, что я понимаю, о чем ты просишь. Этот запрос работает плохо? Вы пытаетесь предотвратить его плохую работу в будущем?
Вы также можете попробовать:
MATCH (c:Characteristic) WHERE id(c) IN {characteristicIds}
WITH collect(c) as characteristics
MATCH (parentP:Product)-[:CONTAINS]->(childP:Product)
WHERE id(parentP) = {productId}
OPTIONAL MATCH childP<-[:VOTED_FOR]-(v:Vote)
WITH childP, v, head(filter(c in characteristics WHERE (v)-[:VOTED_ON]->(c))) as c
WHERE c is not null
WITH childP, c, avg(v.weight) as weight
RETURN childP AS product, sum(weight) as weight
ORDER BY weight DESC
Так как по продукту проголосовало намного меньше, чем голосов за характеристику, мы начинаем с них, а затем проверяем характеристики. Без БД трудно сказать, хотя.
Если мы говорим о проблемах производительности, было бы полезно приложить информацию о профилировании: http://neo4j.com/docs/stable/how-do-i-profile-a-query.html
Тогда будет проще решить, где действовать.