В настоящее время я работаю над рекомендацией фильмов, используя набор данных MovieLens 20m после прочтения https://markorodriguez.com/2011/09/22/a-graph-based-movie-recommender-engine/. Node Movie подключается к Genre с помощью отношения hasGenre, Node Movie подключается к User с помощью отношения hasRating. Я пытаюсь получить все фильмы, которые имеют самый высокий рейтинг (общий рейтинг> 3,0) с запросом (например, История игрушек), которые разделяют все жанры с историей игрушек. Вот мой запрос Cypher:
MATCH (inputMovie:Movie {movieId: 1})-[r:hasGenre]-(h:Genre)
WITH inputMovie, COLLECT (h) as inputGenres
MATCH (inputMovie)<-[r:hasRating]-(User)-[o:hasRating]->(movie)-[:hasGenre]->(genre)
WITH inputGenres, r, o, movie, COLLECT(genre) AS genres
WHERE ALL(h in inputGenres where h in genres) and (r.rating>3 and o.rating>3)
RETURN movie.title,movie.movieId, count(*)
ORDER BY count(*) DESC
Однако похоже, что моя система не может с этим справиться (используя 16 ГБ ОЗУ, Core i7 4-го поколения и SSD). Когда я запускаю запрос, он достигает 97% ОЗУ, после чего Neo4j неожиданно отключается (вероятно, из-за размера кучи или из-за размера ОЗУ).
Заранее спасибо.
Во-первых, ваш Cypher можно упростить для более эффективного планирования, сопоставляя только то, что нам нужно, и обрабатывая остальное в WHERE (так что фильтрация может быть выполнена во время сопоставления)
MATCH (inputMovie:Movie {movieId: 1})-[r:hasGenre]->(h:Genre)
WITH inputMovie, COLLECT (h) as inputGenres
MATCH (inputMovie)<-[r:hasRating]-(User)-[o:hasRating]->(movie)
WHERE (r.rating>3 and o.rating>3) AND ALL(genre in inputGenres WHERE (movie)-[:hasGenre]->(genre))
RETURN movie.title,movie.movieId, count(*)
ORDER BY count(*) DESC
Теперь, если вы не против добавления данных в график, чтобы найти нужные данные, вы можете также разделить запрос на крошечные биты и «кэшировать» результат. Так например
// Cypher 1
MATCH (inputMovie:Movie {movieId: 1})-[r:hasGenre]->(h:Genre)
WITH inputMovie, COLLECT (h) as inputGenres
MATCH (movie:Movie)
WHERE ALL(genre in inputGenres WHERE (movie)-[:hasGenre]->(genre))
// Merge so that multiple runs don't create extra copies
MERGE (inputMovie)-[:isLike]->(movie)
// Cypher 2
MATCH (movie:Movie)<-[r:hasRating]-(user)
WHERE r.rating>3
// Merge so that multiple runs don't create extra copies
MERGE (user)-[:reallyLikes]->(movie)
// Cypher 3
MATCH (inputMovie:Movie{movieId: 1})<-[:reallyLikes]-(user)-[:reallyLikes]->(movie:Movie)<-[:isLike]-(inputMovie)
RETURN movie.title,movie.movieId, count(*)
ORDER BY count(*) DESC
@Adikara с использованием CREATE вместо MERGE будет быстрее (поскольку ему не нужно сначала проверять, что он не существует), но несколько запусков создадут повторяющиеся ссылки. Идея создания ссылок / узлов кеша заключается в том, что это позволяет выполнять такие запросы дешевле, но требует больше времени для настройки. Подходит для случаев, когда ваш исходный запрос не работает из-за нехватки памяти или некоторые части выполняются слишком долго. Однако Cypher 2 не должен создавать никаких узлов. (В переменной была ошибка использования заглавных букв, это исправлено)
Понимаю, есть смысл избегать нехватки памяти. Но после запуска обновления Cypher 2 я получил «Neo.TransientError.General.OutOfMemoryError: Недостаточно памяти для выполнения текущей задачи.», Даже после попытки увеличить размер кучи и размер кеша страницы. После нескольких попыток изменения размера кучи он по-прежнему сталкивается с проблемой нехватки памяти.
Спасибо за ответ @Tezra. Ваш первый Cypher может сократить время до 50%, чем мой, с точным результатом. Однако, используя ваше второе предложение, я не получил никакого результата. Сайфер 1 создает отношения. Cypher 2 создает узлы и отношения. Однако Cypher 3 показывает «(без изменений, без записей)». Общее время Cypher 1 + 2 + 3 на 48% медленнее, чем у первого Cypher, вероятно, потому, что ему нужно физически создавать узлы и все такое. Это может быть очень быстро для следующего запуска, если это сработает.