#neo4j #cypher
#neo4j #cypher
Вопрос:
Недавно я начал набрасывать личный проект, который будет включать в себя социальную сеть. У меня есть некоторый профессиональный опыт работы с Neo4j, и хотя он кажется идеальным, есть один вопрос, который меня беспокоит.
Представьте общую социальную сеть: пользователи подписываются друг на друга, пользователи публикуют сообщения, пользователи могут видеть сообщения, написанные пользователями, на которых они подписаны. Это четко выражено в Neo4j через :User
и :Post
помеченные узлы, связанные через :posted
и :follows
отношения.
Таким образом, я мог бы получать сообщения пользователей, на которые я подписываюсь, используя запрос типа:
MATCH (:User {user_id: 1})-[:follows]->(:User)-[:posted]->(p:Post)
RETURN p
Это довольно чисто и просто. Меня беспокоит то, что реально я захочу получить самые последние 10 сообщений, а затем 10 сообщений после этого и так далее.
Итак, я создал индекс для created_at
поля в :Post
nodes и добавил ORDER BY p.created_at DESC
предложение к запросу. Я думал, что это позволит мне эффективно сортировать их, однако выполнение EXPLAIN
этого запроса показывает, что ORDER BY
предложения по большей части не используют индексы для ускорения этого процесса. Таким образом, я не уверен, есть ли способ получить их эффективно, когда результирующий набор становится значительно большим.
Это может быть из-за неопытности или просто неправильного подхода к этой модели данных. Могу ли я получить какие-либо данные по такого рода проблеме? Должен ли я моделировать свои данные по-другому? Неверен ли мой запрос / индекс? Я чего-то не понимаю? Как бы вы это сделали?
ПРАВКА 1: Пример запроса для чего-то подобного тому, что я имел в виду:
MATCH (:User {user_id: 1})-[:follows]->(:User)-[:posted]->(p:Post)
RETURN p
ORDER BY p.created_at DESC
LIMIT 10
Также я думал, что использование диапазона (в WHERE
предложении) — это возможность ограничить размер результирующего набора, но все еще не уверен, есть ли лучший способ?
ПРАВКА 2 (решение):Это был последний запрос, который заставил планировщик Cypher использовать индекс для решения этой проблемы:
MATCH (:User {user_id: 1})-[:follows]->(:User)-[:posted]->(p:Post)
USING INDEX p:Post(created_at)
WHERE p.created_at < datetime()
RETURN p
ORDER BY p.created_at DESC
LIMIT 10
Комментарии:
1. Можете ли вы показать в своем вопросе запрос, который сортирует сообщения? Кроме того, используете ли вы neo4j 3.5 ?
2. @cybersam Я добавил пример запроса. Это все еще очень гипотетично, и я использую только примерную модель данных. Я знаю, что в версии 3.5 были внесены улучшения в использование индекса по порядку by, однако он довольно ограничен и доступен только в порядке возрастания (противоположном тому, что мне нужно). Если я что-то пропустил, дайте мне знать!
3. Он также поддерживает сортировку по убыванию. Смотрите мой ответ для решения проблемы.
Ответ №1:
В Neo4j 3.5 введена некоторая поддержка использования индексов для выполнения ORDER BY
операций с некоторыми ограничениями.
Но в настоящее время (в neo4j 3.5.3), даже когда для ORDER BY
поддерживается использование индекса, планировщик Cypher, похоже, не использует его автоматически для этой цели. В ходе моих экспериментов с версией 3.5.3 я обнаружил, что если вы не используете индекс в WHERE
предложении, то планировщик вообще не будет использовать индекс.
Итак, в качестве простого обходного пути вы можете просто добавить тривиальное WHERE
предложение, используя индекс. Например, вот модифицированная версия вашего запроса, которая «обманет» планировщика, заставив использовать индекс для ORDER BY
:
MATCH (:User {user_id: 1})-[:follows]->(:User)-[:posted]->(p:Post)
WHERE p.created_at > 0
RETURN p
ORDER BY p.created_at DESC
LIMIT 10
Комментарии:
1. Спасибо за помощь! Профилирование этого запроса на небольшом наборе данных, который я сгенерировал, по-прежнему не использует индекс (100 пользователей, у каждого пользователя 100 сообщений, протестировано на пользователе, который следует за всеми остальными 99 пользователями — выдает 48291 обращение к базе данных за 68 мс). Но я обнаружил, что добавление
USING INDEX p:Post(created_at)
предложения действительно заставляет его использовать индекс, генерируя только 227 обращений к базе данных за 3 мс. Немного странно, насколько сложно заставить Neo4j использовать индексы в этих сценариях. Еще раз, спасибо, что указали мне правильное направление.