2016-09-30 5 views
1

Я довольно новичок в neo4j, и мне сложно оптимизировать запрос, который возвращает большое количество узлов/отношений.Neo4j множественный необязательный запрос соответствия очень медленный

Следующий запрос:

MATCH (u:User)-[:CAN_ADMINISTER]->(cs:CustomerSite) 
WHERE u.id="1234" WITH cs 
MATCH r1=(cs)<-[:AFFECTS_SITE]-(t:Ticket) 
WHERE not(t.status = "COMPLETE") 
OPTIONAL MATCH r2=(t)-[:HAS_EVENTS]->(te:TicketEvent) 
OPTIONAL MATCH r3=(t)-[:CREATED_BY]->(u:User) 
OPTIONAL MATCH r4=(te)<-[:HAS_EVENTS]-(u2:User) 
OPTIONAL MATCH r5=(t)-[:AFFECTS_SITE]->(cs)<-[:HAS_SITE]-(c:Customer) 
RETURN r1, r2, r3, r4, r5 

займет почти минуту, чтобы запустить для пользователя, который производит ~ 7000 строк. Я попытался реорганизовать его, чтобы не сильно помочь. Ниже приведен текущий профиль.

enter image description here

Любые предложения о том, что могло бы помочь здесь?

+0

Первое предложение: используйте PROFILE в этом запросе, а в дереве, сгенерированном профилем, разверните все узлы и вставьте этот профиль в свое описание. Это должно быть общей отправной точкой для определения проблем с производительностью. Кроме того, можете ли вы подтвердить правильность вывода вашего запроса или производить что-либо неожиданное, как в данных, так и в количестве результатов. Кроме того, я предполагаю, что User.id имеет уникальное ограничение ... это правда? – InverseFalcon

+0

Данные действительно верны, и вы правы, User.id является уникальным ограничением. – Greg

+0

Я думаю, что следующая полезная вещь, чтобы выяснить, есть ли какие-либо данные, которые вы возвращаете, которые вам действительно не нужны, и если мы сможем собрать определенные результаты вместо вывода строки за результат. Например, кажется, что вы хотите вернуть Пользователей, у которых есть TicketEvents, связанных с Билетом, и Клиентов, у которых есть сайты, затронутые Билетом. Вам нужно вернуть связанные TicketEvents и сайты, или вам просто нужно знать пользователей и/или клиентов, связанных с этими вещами? И можем ли мы выводить их как коллекции пользователей или клиентов, или вам нужна одна строка для каждой из них? – InverseFalcon

ответ

1

Я настоятельно рекомендую собирать результаты из ВАШИХ ДОКУМЕНТОВ по мере необходимости и использовать WITH, чтобы разбить ваши запросы и сузить колонны, которые вас интересуют, чтобы ваши ряды располагались между вашими подзапросами. Как поясняется в комментариях к вопросу, MATCHES и OPTIONAL MATCHES могут создавать строки результатов, которые могут задавать запросы, которые SEEM, как будто они должны быть более дорогостоящими.

Например, я буду добавлять комментарии анализировать исходный инлайн запроса:

MATCH (u:User)-[:CAN_ADMINISTER]->(cs:CustomerSite) 
WHERE u.id="1234" WITH cs 
MATCH r1=(cs)<-[:AFFECTS_SITE]-(t:Ticket) 
WHERE not(t.status = "COMPLETE") 
// we have 1 row per User at a CustomerSite 
OPTIONAL MATCH r2=(t)-[:HAS_EVENTS]->(te:TicketEvent) 
// now, 1 row per User @ CustomerSite per TicketEvent 
OPTIONAL MATCH r3=(t)-[:CREATED_BY]->(u:User) 
// the above OPTIONAL MATCH had to iterate over each User/CS/TE row instead of just each distinct TICKET 
OPTIONAL MATCH r4=(te)<-[:HAS_EVENTS]-(u2:User) 
// now, 1 row per User @ CustomerSite per User on each Ticket Event 
OPTIONAL MATCH r5=(t)-[:AFFECTS_SITE]->(cs)<-[:HAS_SITE]-(c:Customer) 
// now, 1 row per User @ CustomerSite per User on each Ticket Event per Customer at each Customer Site 
RETURN r1, r2, r3, r4, r5 

В то время как он меняет формат возвращаемых данных, делая Собирает по пути, и заказ ваш ДОПОЛНИТЕЛЬНЫЕ МАТЧАМ лучше, должны улучшить скорость запроса. Вот один из способов вы можете сделать это:

MATCH (u:User)-[:CAN_ADMINISTER]->(cs:CustomerSite) 
WHERE u.id="1234" WITH cs 
MATCH (cs)<-[:AFFECTS_SITE]-(t:Ticket) 
WHERE not(t.status = "COMPLETE") 
// should be 1 creator per ticket, so best to do this first 
OPTIONAL MATCH (t)-[:CREATED_BY]->(creator:User) 
OPTIONAL MATCH (cs)<-[:HAS_SITE]-(affectedCustomer:Customer) 
// collection of affected customers for each ticket (and their creator) affecting a customer site 
WITH cs, t, creator, COLLECT(affectedCustomer) as affectedCustomers 
OPTIONAL MATCH (t)-[:HAS_EVENTS]->(te:TicketEvent)<-[:HAS_EVENTS]-(userOnEvent:User) 
WITH cs, t, creator, affectedCustomers, te, COLLECT(userOnEvent) as usersOnEvent 
RETURN cs, t, creator, affectedCustomers, COLLECT({ticketEvent:te, usersOnEvent:usersOnEvent}) as ticketEventsAndUsers 

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

Дайте попробовать и посмотрите, как сравнивается производительность. Если он выглядит лучше, вам придется изменить способ анализа данных, но ничего не получается для цикла или двух.

+0

Это полезно - спасибо за отличное объяснение – Greg

Смежные вопросы