Schema Diagram and Graph Statistics
Node Statistics
| Node Label | Count |
|---|---|
| Actor | 122,974 |
| Director | 33,592 |
| Movie | 84,661 |
| Review | 2,623,824 |
| Survey | 58,903 |
| Tag | 1,094 |
| User | 247,389 |
Edge Statistics
| Relationship | Count |
|---|---|
| (:Survey)-[:ABOUT_MOVIE]-(:Movie) | 58,903 |
| (:Survey)-[:ABOUT_TAG]-(:Tag) | 58,903 |
| (:Actor)-[:ACTED_IN]-(:Movie) | 371,629 |
| (:User)-[:ANSWERED]-(:Survey) | 58,903 |
| (:Director)-[:DIRECTED]-(:Movie) | 84,294 |
| (:Movie)-[:HAS_REVIEW]-(:Review) | 2,623,824 |
| (:Movie)-[:HAS_TAG]-(:Tag) | 212,092 |
| (:User)-[:RATED]-(:Movie) | 28,454,882 |
Disk space: 7.45 GiB
Total import time: 1,037.15 Seconds
Data Loading
Overview
Data import is handled in two stages: node creation followed by sequential edge ingestion. We put all the csv files under the data import directory of Neo4j. This directory can be set in the neo4j.conf file using the parameter 'dbms.directories.import'.
For the implementation of MovieLens Tag Genome on Neo4j, we used the original files downloaded from their website. We will read the CSV files unpacked from the downloaded file to complete node and edge imports.
Create Indices
To speed up the import process and future queries, we create B+-tree indices for some node labels.
// Unique Movie IDs CREATE CONSTRAINT movie_id_unique IF NOT EXISTS FOR (m:Movie) REQUIRE m.item_id IS UNIQUE; // Unique User IDs CREATE CONSTRAINT user_id_unique IF NOT EXISTS FOR (u:User) REQUIRE u.user_id IS UNIQUE; // Unique Tag IDs CREATE CONSTRAINT tag_id_unique IF NOT EXISTS FOR (t:Tag) REQUIRE t.tag_id IS UNIQUE; // Unique Review IDs CREATE CONSTRAINT review_id_unique IF NOT EXISTS FOR (r:Review) REQUIRE r.review_id IS UNIQUE; // Unique Survey IDs CREATE CONSTRAINT survey_id_unique IF NOT EXISTS FOR (s:Survey) REQUIRE s.survey_id IS UNIQUE; // Unique Director Names CREATE CONSTRAINT director_name_unique IF NOT EXISTS FOR (d:Director) REQUIRE d.name IS UNIQUE; // Unique Actor Names CREATE CONSTRAINT actor_name_unique IF NOT EXISTS FOR (a:Actor) REQUIRE a.name IS UNIQUE;
Movie Node
CALL apoc.periodic.iterate( 'CALL apoc.load.json("file:///movielens/genome_2021/raw/metadata.json")', ' MERGE (m:Movie {item_id: toInteger(value.item_id)}) SET m.title = value.title, m.avgRating = toFloat(value.avgRating), m.imdbId = value.imdbId, m.directedBy = value.directedBy, m.starring = value.starring ', {batchSize: 1000, parallel: true} )
User Nodes and RATED Edges
CALL apoc.periodic.iterate( ' CALL apoc.load.json("file:///movielens/genome_2021/raw/ratings.json") YIELD value RETURN value ', ' MERGE (u:User {user_id: toInteger(value.user_id)}) WITH u, value MATCH (m:Movie {item_id: toInteger(value.item_id)}) CREATE (u)-[:RATED {rating: toFloat(value.rating)}]->(m) ', {batchSize: 1000, parallel: false} );
Tags Node
CALL apoc.periodic.iterate( 'CALL apoc.load.json("file:///movielens/genome_2021/raw/tags.json")', ' MERGE (t:Tag {tag_id: toInteger(value.id)}) SET t.tag = value.tag ', {batchSize: 1000, parallel: true} )
HAS_TAG Edge
CALL apoc.periodic.iterate( 'CALL apoc.load.json("file:///movielens/genome_2021/raw/tag_count.json")', ' MATCH (m:Movie {item_id: toInteger(value.item_id)}) MATCH (t:Tag {tag_id: toInteger(value.tag_id)}) MERGE (m)-[r:HAS_TAG]->(t) SET r.count = toInteger(value.num) ', {batchSize: 1000,parallel: false} )
Review Node and HAS_REVIEW Edge
CALL apoc.periodic.iterate( 'CALL apoc.load.json("file:///movielens/genome_2021/raw/review_with_id.json")', ' MATCH (m:Movie {item_id: toInteger(value.item_id)}) CREATE (r:Review { review_id: toInteger(value.review_id), text: value.txt }) MERGE (m)-[:HAS_REVIEW]->(r) ', {batchSize: 1000, parallel: false} )
Survey Node, ANSWERED, ABOUT_MOVIE, and ABOUT_TAG Edges
CALL apoc.periodic.iterate( 'CALL apoc.load.json("file:///movielens/genome_2021/raw/surveys_id.json") YIELD value', ' MERGE (u:User {user_id: toInteger(value.user_id)}) WITH u, value MATCH (m:Movie {item_id: toInteger(value.item_id)}) MATCH (t:Tag {tag_id: toInteger(value.tag_id)}) CREATE (s:Survey { survey_id: toInteger(value.survey_id), score: toInteger(value.score) }) MERGE (u)-[:ANSWERED]->(s) MERGE (s)-[:ABOUT_MOVIE]->(m) MERGE (s)-[:ABOUT_TAG]->(t) ', {batchSize: 1000, parallel: false} )
Director Node and DIRECTED Edge
CALL apoc.periodic.iterate( ' MATCH (m:Movie) WHERE m.directedBy IS NOT NULL AND trim(m.directedBy) <> "" UNWIND split(m.directedBy, ",") AS raw_director_name WITH m.item_id AS movie_id, trim(raw_director_name) AS director_name WHERE director_name <> "" RETURN movie_id, director_name ', ' MATCH (m:Movie {item_id: movie_id}) MERGE (d:Director {name: director_name}) MERGE (d)-[:DIRECTED]->(m) ', {batchSize: 1000, parallel: false} );
Actor Node and ACTED_IN Edge
CALL apoc.periodic.iterate( ' MATCH (m:Movie) WHERE m.starring IS NOT NULL AND trim(m.starring) <> "" RETURN m, split(m.starring, ",") AS actors ', ' UNWIND actors AS actorName WITH m, trim(actorName) AS name WHERE name <> "" MERGE (a:Actor {name: name}) MERGE (a)-[:ACTED_IN]->(m) ', {batchSize: 1000, parallel: false} )
Queries
Select a query below to view its logic and description.
Q1. Movies Rated by a Selected User
This query lists the movies directly connected to a selected user through outgoing RATED edges.
MATCH (u:User {user_id: 997206})-[r:RATED]->(m:Movie) RETURN m.item_id AS MovieID, m.title AS Movie, r.rating AS Rating ORDER BY Rating DESC
Equivalent query in GQL standard:
MATCH (u:mlgenome_user {user_id: 1})-[r:mlgenome_rated]->(m:mlgenome_movie) RETURN m.item_id AS MovieID, m.title AS Movie, r.rating AS Rating ORDER BY Rating DESC LIMIT 20
Q2. Two-Hop Rating Neighborhood of a User
This query finds distinct users reachable within two RATED hops from a selected user through shared movies.
MATCH p = (u:User {user_id: 997206})-[:RATED*1..2]-(n) WHERE n <> u RETURN DISTINCT labels(n) AS NodeLabels, coalesce(n.title, toString(n.user_id)) AS Neighbor, length(p) AS HopCount ORDER BY HopCount ASC, Neighbor
Equivalent query in GQL standard:
MATCH p = (u:mlgenome_user {user_id: 1})-[:mlgenome_rated]-{1,2}(n) WHERE n != u RETURN DISTINCT labels(n) AS NodeLabels, coalesce(n.title, toString(n.user_id)) AS Neighbor, length(p) AS HopCount ORDER BY HopCount ASC, Neighbor LIMIT 50
Q3. Users Exactly Two Hops Away Through Shared Movies
This query returns users exactly two RATED hops away from a selected user, meaning users who rated at least one same movie.
MATCH (u:User {user_id: 997206})-[:RATED]->(m:Movie)<-[:RATED]-(other:User) WHERE other <> u RETURN other.user_id AS SimilarUser, count(DISTINCT m) AS SharedMovies ORDER BY SharedMovies DESC
Equivalent query in GQL standard:
MATCH (u:mlgenome_user {user_id: 1})-[:mlgenome_rated]->(m:mlgenome_movie)<-[:mlgenome_rated]-(other:mlgenome_user) WHERE other != u RETURN other.user_id AS SimilarUser, count(DISTINCT m) AS SharedMovies ORDER BY SharedMovies DESC LIMIT 20
Q4. High-Rated One-Hop Movie Neighborhood
This query searches a selected user's immediate movie neighborhood while filtering on both the RATED edge value and the movie's average rating.
MATCH (u:User {user_id: 997206})-[r:RATED]->(m:Movie) WHERE r.rating >= 4.0 AND m.avgRating >= 4.0 RETURN m.item_id AS MovieID, m.title AS Movie, r.rating AS UserRating, m.avgRating AS AverageRating ORDER BY UserRating DESC, AverageRating DESC
Equivalent query in GQL standard:
MATCH (u:mlgenome_user {user_id: 1})-[r:mlgenome_rated]->(m:mlgenome_movie) WHERE r.rating >= 4.0 AND m.avgRating >= 4.0 RETURN m.item_id AS MovieID, m.title AS Movie, r.rating AS UserRating, m.avgRating AS AverageRating ORDER BY UserRating DESC, AverageRating DESC LIMIT 20
Q5. Users Who Did NOT Participate in Any Survey
This query identifies users who have not participated in any tag genome surveys.
MATCH (u:User) WHERE NOT (u)-[:ANSWERED]->(:Survey) RETURN u.user_id AS UserID
Equivalent query in GQL standard:
MATCH (u:mlgenome_user) WHERE NOT (u)-[:mlgenome_answered]->(:mlgenome_survey) RETURN u.user_id AS UserID LIMIT 20
Q6. High-Count Movie Tag Edges
This query filters HAS_TAG edges by their count property to find strong movie-tag associations.
MATCH (m:Movie)-[ht:HAS_TAG]->(t:Tag) WHERE ht.count >= 20 RETURN m.title AS Movie, t.tag AS Tag, ht.count AS TagCount ORDER BY TagCount DESC
Equivalent query in GQL standard:
MATCH (m:mlgenome_movie)-[ht:mlgenome_has_tag]->(t:mlgenome_tag) WHERE ht.count >= 20 RETURN m.title AS Movie, t.tag AS Tag, ht.count AS TagCount ORDER BY TagCount DESC LIMIT 20
Q7. User Reachability to a Tag Through Rated Movies
This query checks whether a selected user can reach a selected tag through movies they rated.
MATCH (u:User {user_id: 997206}) MATCH (t:Tag {tag_id: 22}) WITH u, t LIMIT 1 OPTIONAL MATCH p = (u)-[:RATED]->(:Movie)-[:HAS_TAG]->(t) RETURN u.user_id AS UserID, t.tag AS Tag, count(p) > 0 AS Reachable
Equivalent query in GQL standard:
MATCH (u:mlgenome_user {user_id: 1}) MATCH (t:mlgenome_tag) WITH u, t LIMIT 1 OPTIONAL MATCH p = (u)-[:mlgenome_rated]->(:mlgenome_movie)-[:mlgenome_has_tag]->(t) RETURN u.user_id AS UserID, t.tag AS Tag, count(p) > 0 AS Reachable
Q8. Shortest User-to-Tag Explanation Path
This query finds the shortest path from a user to a tag over ratings and movie-tag relationships.
MATCH (u:User {user_id: 997206}) MATCH (t:Tag {tag_id: 22}) WITH u, t LIMIT 1 MATCH p = shortestPath((u)-[:RATED|HAS_TAG*1..4]-(t)) RETURN [n IN nodes(p) | coalesce(n.title, n.tag, toString(n.user_id))] AS Path, length(p) AS Hops
Equivalent query in GQL standard:
MATCH p = SHORTEST (u:mlgenome_user {user_id: 1})-[:mlgenome_rated|mlgenome_has_tag]-{1,4}(t:mlgenome_tag) RETURN p
Q9. Simple Co-Rating Paths Between Users
This query enumerates simple RATED paths up to four hops between one user and another user in the rating graph.
MATCH (source:User {user_id: 997206}) MATCH (target:User) WHERE target <> source WITH source, target LIMIT 1 MATCH p = (source)-[:RATED*1..3]-(target) WHERE all(n IN nodes(p) WHERE single(x IN nodes(p) WHERE x = n)) RETURN [n IN nodes(p) | coalesce(n.title, toString(n.user_id))] AS Path, length(p) AS Hops
Equivalent query in GQL standard:
MATCH p = ACYCLIC (source:mlgenome_user {user_id: 1})-[:mlgenome_rated]-{1,4}(target:mlgenome_user) WHERE target != source RETURN p LIMIT 20
Q10. User-Movie Co-Rating Squares
This query finds two users who both rated the same pair of movies, forming a four-node square in the user-movie rating graph.
MATCH (u1:User)-[:RATED]->(m1:Movie)<-[:RATED]-(u2:User)-[:RATED]->(m2:Movie)<-[:RATED]-(u1) WHERE u1.user_id < u2.user_id AND m1.item_id < m2.item_id RETURN u1.user_id AS User1, u2.user_id AS User2, m1.title AS Movie1, m2.title AS Movie2
Equivalent query in GQL standard:
MATCH (u1:mlgenome_user)-[:mlgenome_rated]->(m1:mlgenome_movie)<-[:mlgenome_rated]-(u2:mlgenome_user)-[:mlgenome_rated]->(m2:mlgenome_movie)<-[:mlgenome_rated]-(u1) WHERE u1.user_id < u2.user_id AND m1.item_id < m2.item_id RETURN u1.user_id AS User1, u2.user_id AS User2, m1.title AS Movie1, m2.title AS Movie2 LIMIT 20
Q11. Movie Star Pattern Across Cast, Tags, and Reviews
This query treats each movie as a star center connected to actors, tags, and reviews, then returns the busiest centers.
MATCH (m:Movie) OPTIONAL MATCH (a:Actor)-[:ACTED_IN]->(m) OPTIONAL MATCH (m)-[:HAS_TAG]->(t:Tag) OPTIONAL MATCH (m)-[:HAS_REVIEW]->(r:Review) WITH m, count(DISTINCT a) AS ActorCount, count(DISTINCT t) AS TagCount, count(DISTINCT r) AS ReviewCount WHERE ActorCount >= 2 AND TagCount >= 2 RETURN m.title AS Movie, ActorCount, TagCount, ReviewCount, ActorCount + TagCount + ReviewCount AS StarDegree ORDER BY StarDegree DESC
Equivalent query in GQL standard:
MATCH (m:mlgenome_movie) OPTIONAL MATCH (a:mlgenome_actor)-[:mlgenome_acted_in]->(m) OPTIONAL MATCH (m)-[:mlgenome_has_tag]->(t:mlgenome_tag) OPTIONAL MATCH (m)-[:mlgenome_has_review]->(r:mlgenome_review) WITH m, count(DISTINCT a) AS ActorCount, count(DISTINCT t) AS TagCount, count(DISTINCT r) AS ReviewCount WHERE ActorCount >= 2 AND TagCount >= 2 RETURN m.title AS Movie, ActorCount, TagCount, ReviewCount, ActorCount + TagCount + ReviewCount AS StarDegree ORDER BY StarDegree DESC LIMIT 20
Q12. Four-Actor Co-Starring Cliques
This query finds four actors who co-star in the same movie, representing a four-clique in the projected co-actor graph.
MATCH (a1:Actor)-[:ACTED_IN]->(m:Movie)<-[:ACTED_IN]-(a2:Actor), (a3:Actor)-[:ACTED_IN]->(m)<-[:ACTED_IN]-(a4:Actor) WHERE a1.name < a2.name AND a2.name < a3.name AND a3.name < a4.name RETURN m.title AS Movie, a1.name AS Actor1, a2.name AS Actor2, a3.name AS Actor3, a4.name AS Actor4
Equivalent query in GQL standard:
MATCH (a1:mlgenome_actor)-[:mlgenome_acted_in]->(m:mlgenome_movie)<-[:mlgenome_acted_in]-(a2:mlgenome_actor), (a3:mlgenome_actor)-[:mlgenome_acted_in]->(m)<-[:mlgenome_acted_in]-(a4:mlgenome_actor) WHERE a1.name < a2.name AND a2.name < a3.name AND a3.name < a4.name RETURN m.title AS Movie, a1.name AS Actor1, a2.name AS Actor2, a3.name AS Actor3, a4.name AS Actor4 LIMIT 20
Q13. Most Similar Users Based on Ratings for the Same Movies (Find pairs of users with overlapping movie preferences. (Collaborative filtering foundation))
This query finds pairs of users who have rated the same movies similarly (within a difference of 1 rating point) and share at least 5 such movie preferences.
MATCH (u1:User)-[r1:RATED]->(m:Movie)<-[r2:RATED]-(u2:User) WHERE u1 <> u2 AND abs(r1.rating - r2.rating) <= 1 WITH u1.user_id AS User1, u2.user_id AS User2, count(DISTINCT m) AS SharedMovies WHERE SharedMovies >= 5 RETURN User1, User2, SharedMovies ORDER BY SharedMovies DESC
Equivalent query in GQL standard:
MATCH (u1:mlgenome_user)-[r1:mlgenome_rated]->(m:mlgenome_movie)<-[r2:mlgenome_rated]-(u2:mlgenome_user) WHERE u1 != u2 AND abs(r1.rating - r2.rating) <= 1 WITH u1.user_id AS User1, u2.user_id AS User2, count(m) AS SharedMovies WHERE SharedMovies >= 5 RETURN User1, User2, SharedMovies ORDER BY SharedMovies DESC LIMIT 10
Q14. User-Survey-Movie-Tag Line Pattern
This query follows a four-node line from users to surveys, then to movies, and finally to tags attached to those movies.
MATCH (u:User)-[:ANSWERED]->(s:Survey)-[:ABOUT_MOVIE]->(m:Movie)-[:HAS_TAG]->(t:Tag) RETURN u.user_id AS UserID, s.survey_id AS SurveyID, m.title AS Movie, t.tag AS Tag, s.score AS SurveyScore
Equivalent query in GQL standard:
MATCH (u:mlgenome_user)-[:mlgenome_answered]->(s:mlgenome_survey)-[:mlgenome_about_movie]->(m:mlgenome_movie)-[:mlgenome_has_tag]->(t:mlgenome_tag) RETURN u.user_id AS UserID, s.survey_id AS SurveyID, m.title AS Movie, t.tag AS Tag, s.score AS SurveyScore LIMIT 20
Q15. Movie Degree Summary Across All Relationship Types
This query computes ungrouped degree statistics for Movie nodes across their adjacent relationships.
MATCH (m:Movie) OPTIONAL MATCH (m)-[]-(n) WITH m, count(DISTINCT n) AS Degree RETURN count(m) AS MovieCount, round(avg(Degree), 2) AS AvgDegree, min(Degree) AS MinDegree, max(Degree) AS MaxDegree
Equivalent query in GQL standard:
MATCH (m:mlgenome_movie) OPTIONAL MATCH (m)-[]-(n) WITH m, count(DISTINCT n) AS Degree RETURN count(m) AS MovieCount, round(avg(Degree), 2) AS AvgDegree, min(Degree) AS MinDegree, max(Degree) AS MaxDegree
Q16. Directors by Average Movie Rating (Filtering to avoid bias from 1-hit wonders, so considering movies >= 3)
This query computes the average rating and movie count for each director with at least 3 movies, sorting them to find the top 10 highest-rated directors.
MATCH (d:Director)-[:DIRECTED]->(m:Movie) WHERE m.avgRating IS NOT NULL WITH d.name AS Director, round(avg(m.avgRating), 2) AS AvgRating, count(m) AS Movies WHERE Movies >= 3 RETURN Director, AvgRating, Movies ORDER BY AvgRating DESC
Equivalent query in GQL standard:
MATCH (d:mlgenome_director)-[:mlgenome_directed]->(m:mlgenome_movie) WHERE m.avgRating IS NOT NULL WITH d.name AS Director, avg(m.avgRating) AS AvgRating, count(m) AS Movies WHERE Movies >= 3 RETURN Director, round(AvgRating, 2) AS AvgRating, Movies ORDER BY AvgRating DESC LIMIT 10
Q17. Actors by Number of Movies Acted In
This query calculates the total number of movies each actor has starred in and returns the top 10 most prolific actors.
MATCH (a:Actor)-[:ACTED_IN]->(m:Movie) RETURN a.name AS Actor, count(m) AS TotalMovies ORDER BY TotalMovies DESC
Equivalent query in GQL standard:
MATCH (a:mlgenome_actor)-[:mlgenome_acted_in]->(m:mlgenome_movie) RETURN a.name AS Actor, count(m) AS TotalMovies ORDER BY TotalMovies DESC LIMIT 10
Q18. Rating Edge Summary
This query summarizes all RATED edges by count and rating-value statistics.
MATCH (:User)-[r:RATED]->(:Movie) RETURN count(r) AS RatingCount, round(avg(r.rating), 2) AS AvgRating, min(r.rating) AS MinRating, max(r.rating) AS MaxRating
Equivalent query in GQL standard:
MATCH (:mlgenome_user)-[r:mlgenome_rated]->(:mlgenome_movie) RETURN count(r) AS RatingCount, round(avg(r.rating), 2) AS AvgRating, min(r.rating) AS MinRating, max(r.rating) AS MaxRating
Q19. Users Who Rate Harshly (Lowest Average Rating)
This query identifies the top 10 users with the lowest average ratings among those who have submitted at least 5 ratings, highlighting the harshest critics.
MATCH (u:User)-[r:RATED]->(m:Movie) WHERE r.rating IS NOT NULL WITH u.user_id AS UserID, count(r) AS TotalRatings, round(avg(r.rating), 2) AS AvgRating WHERE TotalRatings >= 5 RETURN UserID, AvgRating, TotalRatings ORDER BY AvgRating ASC
Equivalent query in GQL standard:
MATCH (u:mlgenome_user)-[r:mlgenome_rated]->(m:mlgenome_movie) WHERE r.rating IS NOT NULL WITH u.user_id AS UserID, count(r) AS TotalRatings, avg(r.rating) AS AvgRating WHERE TotalRatings >= 5 RETURN UserID, round(AvgRating, 2) AS AvgRating, TotalRatings ORDER BY AvgRating ASC LIMIT 10
Q20. Bounded Rating Path Counts by Length
This query counts bounded RATED paths from a selected user and groups the result only by path length.
MATCH p = (:User {user_id: 997206})-[:RATED*1..3]-(target) RETURN length(p) AS PathLength, count(p) AS PathCount, count(DISTINCT target) AS DistinctTargets ORDER BY PathLength ASC
Equivalent query in GQL standard:
MATCH p = (:mlgenome_user {user_id: 1})-[:mlgenome_rated]-{1,3}(target) RETURN length(p) AS PathLength, count(p) AS PathCount, count(DISTINCT target) AS DistinctTargets ORDER BY PathLength ASC
Q21. Bounded Rating Path Counts by Length and Endpoint Type
This query extends path-count aggregation by grouping bounded paths by both path length and endpoint label.
MATCH p = (:User {user_id: 997206})-[:RATED*1..3]-(target) WITH length(p) AS PathLength, labels(target)[0] AS EndpointType, target, p RETURN PathLength, EndpointType, count(p) AS PathCount, count(DISTINCT target) AS DistinctTargets ORDER BY PathLength ASC, PathCount DESC
Equivalent query in GQL standard:
MATCH p = (:mlgenome_user {user_id: 1})-[:mlgenome_rated]-{1,3}(target) WITH length(p) AS PathLength, labels(target)[0] AS EndpointType, target, p RETURN PathLength, EndpointType, count(p) AS PathCount, count(DISTINCT target) AS DistinctTargets ORDER BY PathLength ASC, PathCount DESC
Q22. Directors with the Most Movies Above 4.0 Rating (Not just average, how many great movies have they made? Considered minimum of 2 movies)
This query finds the top 10 directors with the most movies rated 4.0 or above, considering only directors who have at least 2 such highly-rated movies.
MATCH (d:Director)-[:DIRECTED]->(m:Movie) WHERE m.avgRating >= 4.0 WITH d.name AS Director, count(m) AS HighRatedMovies WHERE HighRatedMovies >= 2 RETURN Director, HighRatedMovies ORDER BY HighRatedMovies DESC
Equivalent query in GQL standard:
MATCH (d:mlgenome_director)-[:mlgenome_directed]->(m:mlgenome_movie) WHERE m.avgRating >= 4.0 WITH d.name AS Director, count(m) AS HighRatedMovies WHERE HighRatedMovies >= 2 RETURN Director, HighRatedMovies ORDER BY HighRatedMovies DESC LIMIT 10
Q23. Top Tags by Frequency in Surveys (Which tags are evaluated most often by users?)
This query counts the total number of survey evaluations for each tag and returns the top 10 most frequently evaluated tags.
MATCH (:Survey)-[:ABOUT_TAG]->(t:Tag) RETURN t.tag AS Tag, count(*) AS SurveyCount ORDER BY SurveyCount DESC
Equivalent query in GQL standard:
MATCH (:mlgenome_survey)-[:mlgenome_about_tag]->(t:mlgenome_tag) RETURN t.tag AS Tag, count(*) AS SurveyCount ORDER BY SurveyCount DESC LIMIT 10
Q24. Tags Most Frequently Scored Highly in Surveys (Which tags do users consistently agree are applicable to movies (score ≥ 4)?)
This query identifies tags that users consistently score highly (survey score >= 4) and returns the top 10 highest-scoring tags.
MATCH (s:Survey)-[:ABOUT_TAG]->(t:Tag) WHERE s.score >= 4 RETURN t.tag AS Tag, count(*) AS HighScoreCount ORDER BY HighScoreCount DESC
Equivalent query in GQL standard:
MATCH (s:mlgenome_survey)-[:mlgenome_about_tag]->(t:mlgenome_tag) WHERE s.score >= 4 RETURN t.tag AS Tag, count(*) AS HighScoreCount ORDER BY HighScoreCount DESC LIMIT 10
Q25. Movies With Most Tagged Survey Feedback (Which movies have been analyzed most often in surveys?)
This query finds the top 10 movies that have been evaluated the most number of times in tag genome surveys.
MATCH (s:Survey)-[:ABOUT_MOVIE]->(m:Movie) RETURN m.title AS Movie, count(*) AS SurveyMentions ORDER BY SurveyMentions DESC
Equivalent query in GQL standard:
MATCH (s:mlgenome_survey)-[:mlgenome_about_movie]->(m:mlgenome_movie) RETURN m.title AS Movie, count(*) AS SurveyMentions ORDER BY SurveyMentions DESC LIMIT 10
Q26. Users Who Participated in the Most Surveys
This query lists the top 10 users who have participated in the highest number of tag genome surveys.
MATCH (u:User)-[:ANSWERED]->(s:Survey) RETURN u.user_id AS User, count(s) AS TotalSurveys ORDER BY TotalSurveys DESC
Equivalent query in GQL standard:
MATCH (u:mlgenome_user)-[:mlgenome_answered]->(s:mlgenome_survey) RETURN u.user_id AS User, count(s) AS TotalSurveys ORDER BY TotalSurveys DESC LIMIT 10
Q27. PageRank on the MovieLens Genome Rating Graph
This query runs PageRank over an undirected User-Movie rating graph projection and returns the highest-scoring nodes.
CALL gds.graph.project( 'MLGenomeRatingGraphPageRank', ['User', 'Movie'], {RATED: {type: 'RATED', orientation: 'UNDIRECTED'}} ); CALL gds.pageRank.stream('MLGenomeRatingGraphPageRank') YIELD nodeId, score RETURN labels(gds.util.asNode(nodeId)) AS Labels, coalesce(gds.util.asNode(nodeId).title, toString(gds.util.asNode(nodeId).user_id)) AS Node, score ORDER BY score DESC
Q28. Weakly Connected Components of the Rating Graph
This query finds weakly connected components in the undirected User-Movie rating graph projection.
CALL gds.graph.project( 'MLGenomeRatingGraphWcc', ['User', 'Movie'], {RATED: {type: 'RATED', orientation: 'UNDIRECTED'}} ); CALL gds.wcc.stream('MLGenomeRatingGraphWcc') YIELD nodeId, componentId RETURN componentId, count(*) AS ComponentSize ORDER BY ComponentSize DESC
Q29. Strongly Connected Components of Directed Rating Edges
This query treats RATED edges as directed from users to movies and reports strongly connected component sizes.
Q30. Louvain Communities in the Rating Graph
This query detects communities in the undirected User-Movie rating graph using Louvain and returns the largest communities.
CALL gds.graph.project( 'MLGenomeRatingGraphLouvain', ['User', 'Movie'], {RATED: {type: 'RATED', orientation: 'UNDIRECTED'}} ); CALL gds.louvain.stream('MLGenomeRatingGraphLouvain') YIELD nodeId, communityId RETURN communityId, count(*) AS CommunitySize ORDER BY CommunitySize DESC
Q31. Best Tags per Movie by Survey and Tag_Count
This query correlates tags applied to movies via direct tag counts with survey evaluations to find movie-tag pairings with the highest average consensus score.
MATCH (m:Movie)-[ht:HAS_TAG]->(t:Tag) MATCH (s:Survey)-[:ABOUT_MOVIE]->(m) MATCH (s)-[:ABOUT_TAG]->(t) WITH m.title AS Movie, t.tag AS Tag, sum(ht.count) AS TagApplications, avg(s.score) AS AvgSurveyScore, count(s) AS SurveyMentions WHERE TagApplications > 5 AND SurveyMentions >= 5 RETURN Movie, Tag, TagApplications, round(AvgSurveyScore, 2) AS AvgSurveyScore, SurveyMentions ORDER BY AvgSurveyScore DESC, SurveyMentions DESC
Equivalent query in GQL standard:
MATCH (m:mlgenome_movie)-[ht:mlgenome_has_tag]->(t:mlgenome_tag) MATCH (s:mlgenome_survey)-[:mlgenome_about_movie]->(m) MATCH (s)-[:mlgenome_about_tag]->(t) WITH m.title AS Movie, t.tag AS Tag, sum(ht.count) AS TagApplications, avg(s.score) AS AvgSurveyScore, count(s) AS SurveyMentions WHERE TagApplications > 5 AND SurveyMentions >= 5 RETURN Movie, Tag, TagApplications, round(AvgSurveyScore, 2) AS AvgSurveyScore, SurveyMentions ORDER BY AvgSurveyScore DESC, SurveyMentions DESC LIMIT 20
Q32. Top Actor-Director Pairs by Number of Collaborations
This query finds the top 10 actor-director pairs with the highest number of movie collaborations.
MATCH (d:Director)-[:DIRECTED]->(m:Movie)<-[:ACTED_IN]-(a:Actor) RETURN d.name AS Director, a.name AS Actor, count(m) AS Collaborations ORDER BY Collaborations DESC
Equivalent query in GQL standard:
MATCH (d:mlgenome_director)-[:mlgenome_directed]->(m:mlgenome_movie)<-[:mlgenome_acted_in]-(a:mlgenome_actor) RETURN d.name AS Director, a.name AS Actor, count(m) AS Collaborations ORDER BY Collaborations DESC LIMIT 10
Q33. Directors Whose Movies Collect the Most Reviews
This query finds the top 10 directors whose movies have collectively accumulated the highest number of text reviews.
MATCH (d:Director)-[:DIRECTED]->(m:Movie)-[:HAS_REVIEW]->(r:Review) RETURN d.name AS Director, count(r) AS TotalReviews ORDER BY TotalReviews DESC
Equivalent query in GQL standard:
MATCH (d:mlgenome_director)-[:mlgenome_directed]->(m:mlgenome_movie)-[:mlgenome_has_review]->(r:mlgenome_review) RETURN d.name AS Director, count(r) AS TotalReviews ORDER BY TotalReviews DESC LIMIT 10
Q34. Actor-Director Collaboration Quality (Which pairs consistently produce great movies?)
This query evaluates the quality of actor-director collaborations by computing the average rating of their shared movies, returning the top 10 pairs with at least 3 collaborations.
MATCH (d:Director)-[:DIRECTED]->(m:Movie)<-[:ACTED_IN]-(a:Actor) WHERE m.avgRating IS NOT NULL WITH d.name AS Director, a.name AS Actor, count(m) AS Collabs, round(avg(m.avgRating), 2) AS AvgRating WHERE Collabs >= 3 RETURN Director, Actor, Collabs, AvgRating ORDER BY AvgRating DESC
Equivalent query in GQL standard:
MATCH (d:mlgenome_director)-[:mlgenome_directed]->(m:mlgenome_movie)<-[:mlgenome_acted_in]-(a:mlgenome_actor) WHERE m.avgRating IS NOT NULL WITH d.name AS Director, a.name AS Actor, count(m) AS Collabs, avg(m.avgRating) AS AvgRating WHERE Collabs >= 3 RETURN Director, Actor, Collabs, round(AvgRating, 2) AS AvgRating ORDER BY AvgRating DESC LIMIT 10
Q35. Tag-Movie Associations with High Confidence (Which movie-tag pairs were most confidently agreed upon by users?)
This query finds movie-tag pairs that are strongly agreed upon by users, based on the frequency of high survey scores (score >= 4).
MATCH (s:Survey)-[:ABOUT_MOVIE]->(m:Movie), (s)-[:ABOUT_TAG]->(t:Tag) WHERE s.score >= 4 RETURN m.title AS Movie, t.tag AS Tag, count(*) AS HighScoreCount ORDER BY HighScoreCount DESC
Equivalent query in GQL standard:
MATCH (s:mlgenome_survey)-[:mlgenome_about_movie]->(m:mlgenome_movie), (s)-[:mlgenome_about_tag]->(t:mlgenome_tag) WHERE s.score >= 4 RETURN m.title AS Movie, t.tag AS Tag, count(*) AS HighScoreCount ORDER BY HighScoreCount DESC LIMIT 10
Q36. Actors Frequently Appearing in High-Rated Movies with Different Directors (Highlights high-performing, collaborative actors trusted by many top directors.)
This query finds actors who frequently appear in highly-rated movies (rating >= 4.0) with different directors, showcasing versatile and collaborative actors.
MATCH (a:Actor)-[:ACTED_IN]->(m:Movie)<-[:DIRECTED]-(d:Director) WHERE m.avgRating >= 4.0 WITH a.name AS Actor, count(DISTINCT d.name) AS UniqueTopDirectors, count(DISTINCT m) AS TopRatedMovies WHERE TopRatedMovies >= 3 RETURN Actor, UniqueTopDirectors, TopRatedMovies ORDER BY UniqueTopDirectors DESC, TopRatedMovies DESC
Equivalent query in GQL standard:
MATCH (a:mlgenome_actor)-[:mlgenome_acted_in]->(m:mlgenome_movie)<-[:mlgenome_directed]-(d:mlgenome_director) WHERE m.avgRating >= 4.0 WITH a.name AS Actor, count(DISTINCT d.name) AS UniqueTopDirectors, count(DISTINCT m) AS TopRatedMovies WHERE TopRatedMovies >= 3 RETURN Actor, UniqueTopDirectors, TopRatedMovies ORDER BY UniqueTopDirectors DESC, TopRatedMovies DESC LIMIT 10
Q37. Most Frequently Used Words in Reviews
This query tokenizes text reviews to extract the most frequently used words, filtering out common stop words.
MATCH (r:Review) WHERE r.text IS NOT NULL UNWIND apoc.text.split(toLower(r.text), "\W+") AS word WITH word WHERE size(word) > 3 AND NOT word IN [ "this", "that", "with", "from", "have", "your", "just", "what", "when", "they", "were", "then", "there", "would", "which", "could", "really" ] RETURN word, count(*) AS freq ORDER BY freq DESC LIMIT 20
split) and no list-unnesting clause
(UNWIND) with which to expand the words of a review into rows.
Q38. Movies Most Frequently Called "Boring", "Disgusting", etc.
This query counts reviews containing negative keywords (e.g., 'boring', 'disgusting', 'waste') for each movie to identify those with the most negative feedback.
MATCH (m:Movie)-[:HAS_REVIEW]->(r:Review) WHERE toLower(r.text) CONTAINS 'boring' OR toLower(r.text) CONTAINS 'disgusting' OR toLower(r.text) CONTAINS 'waste' OR toLower(r.text) CONTAINS 'terrible' OR toLower(r.text) CONTAINS 'bad' WITH m.title AS Movie, count(*) AS NegativeMentions RETURN Movie, NegativeMentions ORDER BY NegativeMentions DESC
Equivalent query in GQL standard:
MATCH (m:mlgenome_movie)-[:mlgenome_has_review]->(r:mlgenome_review) WHERE toLower(r.text) CONTAINS 'boring' OR toLower(r.text) CONTAINS 'disgusting' OR toLower(r.text) CONTAINS 'waste' OR toLower(r.text) CONTAINS 'terrible' OR toLower(r.text) CONTAINS 'bad' WITH m.title AS Movie, count(*) AS NegativeMentions RETURN Movie, NegativeMentions ORDER BY NegativeMentions DESC LIMIT 10
Q39. Users With Highest Survey Agreement Accuracy (Measures users whose survey scores closely align with consensus (like those who gave 5 when others did too))
This query ranks users by how closely their survey ratings align with the consensus ratings for each tag, highlighting the most consistent/accurate survey responders.
MATCH (u:User)-[:ANSWERED]->(s:Survey)-[:ABOUT_TAG]->(t:Tag) WITH u.user_id AS User, t.tag AS Tag, s.score AS UserScore MATCH (s2:Survey)-[:ABOUT_TAG]->(t) WHERE s2.score IS NOT NULL WITH User, Tag, UserScore, avg(s2.score) AS Consensus WITH User, avg(abs(UserScore - Consensus)) AS AvgDeviation, count(*) AS Count WHERE Count >= 10 RETURN User, round(AvgDeviation, 2) AS AvgTagDisagreement, Count ORDER BY AvgTagDisagreement ASC
Equivalent query in GQL standard:
MATCH (u:mlgenome_user)-[:mlgenome_answered]->(s:mlgenome_survey)-[:mlgenome_about_tag]->(t:mlgenome_tag) WITH u.user_id AS User, t.tag AS Tag, s.score AS UserScore MATCH (s2:mlgenome_survey)-[:mlgenome_about_tag]->(t) WHERE s2.score IS NOT NULL WITH User, Tag, UserScore, avg(s2.score) AS Consensus WITH User, avg(abs(UserScore - Consensus)) AS AvgDeviation, count(*) AS Count WHERE Count >= 10 RETURN User, round(AvgDeviation, 2) AS AvgTagDisagreement, Count ORDER BY AvgTagDisagreement ASC LIMIT 10