#neo4j #cypher
#neo4j #шифр
Вопрос:
Есть предложения по повышению производительности путем улучшения структуры графа или запроса следующего? В идеале я бы хотел, чтобы это было под 1s. На данный момент лучшее, что я могу получить, это ~ 8 секунд с 7 миллионами посещений db примерно на 2 МИЛЛИОНА узлов и 10 МИЛЛИОНОВ rels.
У меня есть структура графа, где
(co:Company)<-[:HAS_PROVIDER]-(c:Customer)-[:HAS_CLAIM]->(c:Claim)
- каждое утверждение имеет логическое свойство «direct», а также свойство, называемое «сумма»
- у каждой компании есть имя
- Клиенты могут иметь несколько заявок, но только одна компания
Я хочу иметь возможность показывать:
- различное название компании
- количество клиентов для компании
- количество прямых = истинных утверждений
- сумма сумм для прямых = истинных утверждений
- количество прямых = ложных утверждений
- сумма суммы для прямых = ложных утверждений
Для достижения этой цели я использовал два подхода:
а) создал связь от клиента к заявке, где direct=true как :IS_DIRECT и direct= false как:IS_INDIRECT
б) помечено каждое утверждение direct = true как узел:DirectClaim, а утверждение direct = false как узел:InDirectClaim
использование (a) позволяет мне получить название компании, количество клиентов, размер (IS_DIRECT) и размер (IS_INDIRECT) с помощью фильтра по ТИПУ rels. однако получение суммы (суммы) с использованием комбинаций извлечения, фильтрации, уменьшения времени ожидания независимо от конфигурации.
использование (b) работает, но занимает ~ 10 секунд
Редактировать:
запрос для (a) выглядит следующим образом (с подсказкой для @cybersam это теперь ~ 6s)
MATCH (co:Company)<-[:HAS_PROVIDER]-(c)-[r:IS_DIRECT|IS_INDIRECT]->(cl)
WITH distinct co, collect(r) as rels, count (distinct c) as cntc, collect(mc) as claims
WITH co, cntc,
size(filter(r in rels WHERE TYPE(r) = 'IS_DIRECT')) as dcls,
size(filter(r in rels WHERE TYPE(r) = 'IS_INDIRECT')) as indcls,
REDUCE
(s = {dclsamt: 0, indclsamt: 0}, x IN claims |
CASE WHEN x.direct
THEN {dclsamt: s.dclsamt x.amount, indclsamt: s.indclsamt}
ELSE {dclsamt: s.dclsamt, indclsamt: s.indclsamt x.amount}
END)
AS data
RETURN co.name as name,cntc, dcls,indcls, data
ORDER BY dcls desc
запрос для (b) выглядит следующим образом:
MATCH (co:Company)<-[:HAS_PROVIDER]-(c)-[:HAS_CLAIM]->(cl)
WITH distinct co, count (distinct c) as cntc, COLLECT(cl) as cls
WITH co,cntc,
FILTER(n in cls WHERE 'DirectClaim' IN IN LABELS(n)) as dcls,
FILTER(n in cls WHERE 'InDirectClaim' IN LABELS(n)) as indcls
WITH co,cntc, size(dcls) as dclsct, size(indcls) as indclsct,
REDUCE(s = 0 , x IN dcls | s x.amount) as dclsamt,
REDUCE(s = 0 , x IN indcls | s x.amount) as indclsamt
RETURN co.name as name, cntc, dclsct, dclsamt, indclsct, indclsamt
Комментарии:
1. Можете ли вы предоставить запросы, которые вы используете в настоящее время для обоих подходов a и b?
2. @InverseFalcon добавлены запросы
Ответ №1:
Нет необходимости добавлять дополнительные данные (например, избыточные связи или метки) в вашу модель данных.
Этот запрос показывает один из способов возврата желаемых результатов (в возвращаемой data
карте t
это сумма true
сумм и tc
количество true
сумм; аналогично для f
и fc
):
MATCH (co:Company)<-[:HAS_PROVIDER]-(cu:Customer)-[:HAS_CLAIM]->(cl:Claim)
WITH co.name as comp_name, COUNT(DISTINCT cu) AS cust_count,
REDUCE(s = {t: 0, tc: 0, f: 0, fc: 0}, x IN COLLECT(cl) |
CASE WHEN x.direct
THEN {t: s.t x.amount, tc: s.tc 1, f: s.f, fc: s.fc}
ELSE {t: s.t, tc: s.tc, f: s.f x.amount, fc: s.fc 1}
END) AS data
RETURN comp_name, cust_count, data
Комментарии:
1. этот запрос пока самый быстрый. ~ 6 локальных и ~ 10 секунд на удаленном экземпляре
Ответ №2:
Вашей текущей модели данных более чем достаточно для получения желаемых результатов.
MATCH (co:Company)<-[:HAS_PROVIDER]-(cust:Customer)-[:HAS_CLAIM]->(claim:Claim)
WITH co, COUNT(DISTINCT cust) AS custs, COLLECT(DISTINCT claim) AS claims
WITH co,
custs,
[x IN claims WHERE x.direct|x.amount] AS direct_amts,
[x IN claims WHERE NOT x.direct|x.amount] AS indirect_amts
RETURN co,
custs,
SIZE(direct_amts) AS direct_count,
REDUCE(s=0, x IN direct_amts| s x) AS direct_amt_total,
SIZE(indirect_amts) AS indirect_count,
REDUCE(s=0, x IN indirect_amts| s x) AS indirect_amt_total
Если вам действительно нужна скорость, убедитесь, что у вас есть индекс :Claim(direct)
и :Claim(amount)
, и это действительно будет кричать. В качестве альтернативы, превратите логическое свойство во вторую метку (т. Е. (claim:Claim:Direct)
), и вы сможете сохранить себе индекс.
ОБНОВЛЕНИЕ: исходя из того, что вы цитируете, ваши единственные реальные возможности для улучшения будут основываться на вашем профиле использования. Если этот график является «живым» и будет постоянно обновляться, вы всегда можете кэшировать количество и общую сумму на :Customer
узле всякий раз, когда вы добавляете, удаляете или изменяете a :Claim
. Вот где сияют графики, небольшие частые запросы, которые касаются подмножества базы данных. Поэтому, когда вы делаете что-либо с утверждением, повторно запустите агрегацию только для соответствующего :Customer
, сохраните результаты в свойствах Customer
as, а затем для вашего большого отчета просто извлеките эти свойства непосредственно из (гораздо меньшего числа) :Customer
узлов.
Комментарии:
1. Спасибо. Ваш второй запрос действительно описывает то, что мне нужно, и я использовал извлечение / фильтр за один раз в нескольких предыдущих попытках. Кроме того, я добавил индекс для «прямого», к вашему сведению, я добавил свои «лучшие» попытки запроса к исходному вопросу. все эти запросы по-прежнему выполняются не лучше, чем ~ 7 секунд