Я настоятельно рекомендую собирать результаты из ВАШИХ ДОКУМЕНТОВ по мере необходимости и использовать 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
Каждая строка будет соответствовать с билетом на сайте клиента, создателя билета, сбор пострадавших клиентов на сайте, а также сбор событий билета за билет и для события - пользователи этого события.
Дайте попробовать и посмотрите, как сравнивается производительность. Если он выглядит лучше, вам придется изменить способ анализа данных, но ничего не получается для цикла или двух.
Первое предложение: используйте PROFILE в этом запросе, а в дереве, сгенерированном профилем, разверните все узлы и вставьте этот профиль в свое описание. Это должно быть общей отправной точкой для определения проблем с производительностью. Кроме того, можете ли вы подтвердить правильность вывода вашего запроса или производить что-либо неожиданное, как в данных, так и в количестве результатов. Кроме того, я предполагаю, что User.id имеет уникальное ограничение ... это правда? – InverseFalcon
Данные действительно верны, и вы правы, User.id является уникальным ограничением. – Greg
Я думаю, что следующая полезная вещь, чтобы выяснить, есть ли какие-либо данные, которые вы возвращаете, которые вам действительно не нужны, и если мы сможем собрать определенные результаты вместо вывода строки за результат. Например, кажется, что вы хотите вернуть Пользователей, у которых есть TicketEvents, связанных с Билетом, и Клиентов, у которых есть сайты, затронутые Билетом. Вам нужно вернуть связанные TicketEvents и сайты, или вам просто нужно знать пользователей и/или клиентов, связанных с этими вещами? И можем ли мы выводить их как коллекции пользователей или клиентов, или вам нужна одна строка для каждой из них? – InverseFalcon