Skip to content

calinconstantinov/neo4jworkshop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

75 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Neo4j Workshop

Spring Boot Spring Data Java Neo4j Workshop

Tested with Neo4j 4.0.11

Start a local container
docker run -p 7474:7474 -p 7687:7687 --name neo4j-apoc -e NEO4J_AUTH=neo4j/admin -e NEO4J_dbms_security_procedures_unrestricted=apoc.\* -e NEO4J_apoc_export_file_enabled=true -e NEO4J_apoc_import_file_enabled=true -e NEO4J_apoc_import_file_use__neo4j__config=true -e NEO4JLABS_PLUGINS=["apoc"] neo4j:4.0.11

Database setup

  1. GraphML import
    CALL apoc.import.graphml('https://raw.githubusercontent.com/calinconstantinov/neo4jworkshop/master/src/main/resources/db/sample.graphml', {readLabels: true})

  2. Cypher import (schema)
    Copy the one in: https://raw.githubusercontent.com/calinconstantinov/neo4jworkshop/master/src/main/resources/db/schema.cyp

  3. Schema: Constraints
    CALL db.constraints

  4. Schema: Indexes
    CALL db.indexes

  5. Schema: Meta-Graph
    CALL apoc.meta.graph

  6. APOC Schema
    CALL apoc.meta.schema

  7. Delete all data
    MATCH (n)
    DETACH DELETE n

  8. Delete schema
    CALL apoc.schema.assert({},{})

GRAph Style Sheet customization

  1. Export GRASS file
    :style

  2. Import GRASS file
    Drag and drop the one in: https://raw.githubusercontent.com/calinconstantinov/neo4jworkshop/master/src/main/resources/db/style.grass

  3. Reset GRASS
    :style reset

Database export
Change 'file' to a locally writable file.

  1. Export CSV
    CALL apoc.export.csv.all('file', {})

  2. Export Cypher Schema
    CALL apoc.export.cypher.schema('file', {format:'plain'})

  3. Export ALL Cypher
    CALL apoc.export.cypher.all('file', {format:'plain'})

  4. Export GraphML
    CALL apoc.export.graphml.all('file', {useTypes:true})

Sample queries

  1. Retrieve a user
    MATCH (n:User)
    WHERE n.name='Calin'
    RETURN n

  2. Matching authentication
    MATCH (n:User)-[:HAS_AUTHENTICATION]-(c)
    WHERE n.name='Calin'
    RETURN n, c

  3. Retrieving all users
    MATCH (n:User)
    RETURN n

  4. Retrieve Calin's friends
    MATCH (n:User)-[:FRIENDS_WITH]-(friend)
    WHERE n.name = 'Calin'
    RETURN n, friend

  5. Retrieve all friendship pairs once
    MATCH (n:User)-[:FRIENDS_WITH]->(friend)
    WHERE ID(n) > ID(friend)
    RETURN n, friend

  6. Remove duplicate friendship relationships
    MATCH (u1:User)-[friendship:FRIENDS_WITH]->(u2:User) WHERE ID(u1) > ID(u2) DELETE friendship

  7. Retrieve friendships lists
    MATCH (n:User)-[:FRIENDS_WITH]-(friend)
    RETURN n.name, collect(friend.name), count(friend) AS numberOfFriends
    ORDER BY numberOfFriends DESC

  8. Include authentication with friendships lists
    MATCH (a:Authentication)<-[:HAS_AUTHENTICATION]-(n:User)-[:FRIENDS_WITH]-(friend)
    RETURN n.name, count(friend)

  9. Optional matching authentication with friendships lists
    MATCH (n:User)-[:FRIENDS_WITH]-(friend)
    OPTIONAL MATCH (n)-[]-(a:Authentication)
    RETURN a.email, n.name, count(friend)

  10. Matching Calin's posts
    MATCH (n:User)
    WHERE n.name = 'Calin'
    WITH n
    MATCH (p:Post)-[:POSTED_BY]->(n)
    RETURN n, p

  11. Matching Calin's posts' likes
    MATCH (n:User)<-[:POSTED_BY]-(p:Post)<-[like:LIKES_POST]-(liker:User)
    WHERE n.name = 'Calin'
    RETURN n, p, like, liker

  12. Matching non-narcissistic likes
    MATCH (n:User)<-[:POSTED_BY]-(p:Post)<-[like:LIKES_POST]-(liker:User)
    WHERE n.name = 'Calin' AND n.uuid <> liker.uuid
    RETURN n.name, p.content, like.timestamp, liker.name

  13. Matching comments on Calin's posts
    MATCH (n:User)
    WHERE n.name = 'Calin'
    WITH n
    MATCH (commenter:User)-[:COMMENTED]-(c:Comment)-[:ON_POST]->(p:Post)-[:POSTED_BY]->(n)
    RETURN n, p, c, commenter

  14. Now match all the way to reactions
    MATCH (user:User)
    WHERE user.name = 'Calin'
    WITH user
    MATCH (user)<-[:POSTED_BY]-(post:Post)<-[:ON_POST]-(comment:Comment)<-[:COMMENTED]-(commenter:User),
    (comment)<-[:AT_COMMENT]-(reaction:Reaction)-[:OF_TYPE]->(reactionType:ReactionType),
    (reaction)<-[:REACTED]-(reacter:User)
    RETURN user, post, comment, commenter, reaction, reactionType, reacter

  15. Debugging an incorrect query
    PROFILE
    MATCH (user:User)
    WHERE user.name = 'Calin'
    WITH user
    MATCH (user)<-[:POSTED_BY]-(post:Post)<-[:ON_POST]-(comment:Comment)<-[:COMMENTED]-(commenter:User),
    (comment)<-[:AT_COMMENT]-(reaction:Reaction)-[:OF_TYPE]->(reactionType:ReactionType),
    (r)<-[:REACTED]-(reacter:User)
    RETURN user, post, comment, commenter, reaction, reactionType, reacter

  16. Include comments with no reactions
    MATCH (user:User)
    WHERE user.name = 'Calin'
    WITH user
    MATCH (user)<-[:POSTED_BY]-(post:Post)<-[:ON_POST]-(comment:Comment)<-[:COMMENTED]-(commenter:User) OPTIONAL MATCH (comment)<-[:AT_COMMENT]-(reaction:Reaction)-[:OF_TYPE]->(reactionType:ReactionType),
    (reaction)<-[:REACTED]-(reacter:User)
    RETURN user, post, comment, commenter, reaction, reactionType, reacter

  17. Finally match all social data related to 'Calin'
    MATCH (user:User)
    WHERE user.name = 'Calin'
    WITH user
    MATCH (user)<-[:POSTED_BY]-(post:Post)<-[:ON_POST]-(comment:Comment)<-[:COMMENTED]-(commenter:User) OPTIONAL MATCH (comment)<-[:AT_COMMENT]-(reaction:Reaction)-[:OF_TYPE]->(reactionType:ReactionType)
    OPTIONAL MATCH (reaction)<-[:REACTED]-(reacter:User)
    OPTIONAL MATCH (replier:User)-[:COMMENTED]->(reply:Comment)-[:REPLIED_TO]->(comment)
    RETURN user, post, comment, commenter, reaction, reactionType, reacter, reply, replier

  18. But where do Calin's friends work?
    MATCH (user:User)
    WHERE user.name = 'Calin'
    WITH user
    MATCH (user)-[:FRIENDS_WITH]-(friend)<-[:EMPLOYED]-(company:Company)
    RETURN friend.name, company.name

  19. Match all data
    MATCH (n)
    RETURN n

Handling time

  1. Time data model
    MATCH (y:Year)-[:HAS_MONTH]->(m:Month)-[:HAS_DAY]->(d:Day)-[:HAS_HOUR]->(h:Hour)
    RETURN y, m, d, h

  2. Ordering time
    MATCH (y:Year)-[:HAS_MONTH]->(m:Month)-[:HAS_DAY]->(d:Day)-[:HAS_HOUR]->(h:Hour)
    RETURN y.uuid, m.uuid, d.uuid, h.uuid
    ORDER BY y.uuid ASC, m.uuid ASC, d.uuid ASC, h.uuid ASC

  3. Get a sequence of hours:
    MATCH (firstHour:Hour)-[:NEXT_HOUR*..5]->(h)
    WHERE firstHour.uuid = '2021051522'
    WITH firstHour + collect(h) as dayList
    UNWIND dayList as day
    RETURN day

  4. Get a sequence of hours including corresponding days:
    MATCH (firstHour:Hour)-[:NEXT_HOUR*..5]->(h)
    WHERE firstHour.uuid = '2021051522'
    WITH firstHour + collect(h) as hourList
    UNWIND hourList as hour
    WITH hour MATCH (hour)<-[:HAS_HOUR]-(day:Day)
    RETURN day, hour

Data analysis

  1. Friendship relationships between companies
    MATCH (c:Company)-[:EMPLOYED]->(u:User)-[:FRIENDS_WITH]-(u2:User)<-[:EMPLOYED]-(c2:Company)
    WHERE c.uuid <> c2.uuid AND c.name = 'Endava'
    RETURN c2.name as company, count(DISTINCT u2) as friendsOfEmployees
    ORDER BY friendsOfEmployees DESC

  2. Computing Post Power

    1. Adding specific Post Power node
      MATCH (post:Post)
      WITH collect(post) as posts
      FOREACH (post in posts |
      CREATE (pP:PostPower {postPower: 0})
      MERGE (pP)<-[:HAS_POWER]-(post))

    2. Computing and attaching Post Power value
      MATCH (post:Post)-[:POSTED_BY]->(poster:User)
      WITH post, poster
      OPTIONAL MATCH (post)<-[like:LIKES_POST]-(liker:User)
      WHERE poster.uuid <> liker.uuid
      WITH post, poster, count(like) as likes
      OPTIONAL MATCH (post)<-[:ON_POST]-(comment:Comment)<-[:COMMENTED]-(commenter:User)
      WHERE poster.uuid <> commenter.uuid
      WITH post, poster, likes, count(comment) as comments
      OPTIONAL MATCH (post)<-[:ON_POST]-(comment)<-[:AT_COMMENT]-(reaction:Reaction)<-[:REACTED]-(reactor:User)
      WHERE poster.uuid <> reactor.uuid
      WITH post, poster, likes, comments, count(reaction) as reactions
      OPTIONAL MATCH (post)<-[:ON_POST]-(comment)<-[:REPLIED_TO]-(reply:Comment)<-[:COMMENTED]-(replier:User)
      WHERE poster.uuid <> replier.uuid
      WITH post, poster, likes, comments, reactions, count(reply) as replies
      WITH poster.name as posterName, post, likes, comments, reactions, replies, 1 * likes + 2 * reactions + 3 * comments + 4 * replies AS postPower
      MATCH (post)-[:HAS_POWER]->(pp:PostPower) SET pp.postPower = postPower RETURN posterName, post.content, likes, comments, reactions, replies, postPower ORDER BY postPower DESC

  3. BFFs <3
    MATCH (user:User)
    WHERE user.name = 'Calin'
    WITH user
    MATCH (user)-[:FRIENDS_WITH]-(friend)
    WITH user, friend
    OPTIONAL MATCH (user)<-[:POSTED_BY]-(post:Post)<-[l:LIKES_POST]-(friend) WITH user, friend, count(l) as likes
    OPTIONAL MATCH (user)-[:COMMENTED]->(comment:Comment)<-[:AT_COMMENT]-(reaction:Reaction)-[:REACTED]-(friend)
    WITH user, friend, likes, count(reaction) as reactions
    OPTIONAL MATCH (user)<-[:POSTED_BY]-(post:Post)<-[:ON_POST]-(comment:Comment)<-[:COMMENTED]-(friend)
    WITH user, friend, likes, reactions, count(comment) as comments, reactions + 2 * likes + 3 * count(comment) as friendshipPower
    WHERE friendshipPower <> 0
    RETURN friend.name, likes, reactions, comments, friendshipPower
    ORDER BY friendshipPower DESC

Full-text indexes powered by the Apache Lucene

  1. Create full-text index
    CALL db.index.fulltext.createNodeIndex("user_names",["User"],["name"]);

  2. Term query example
    CALL db.index.fulltext.queryNodes("user_names", "Calin Mihai") YIELD node, score RETURN node.name, score

  3. Fuzzy query example
    CALL db.index.fulltext.queryNodes("user_names", "clin~") YIELD node, score RETURN node.name, score

Releases

No releases published

Packages

No packages published

Languages