ALEXANDRE LOUVET


Sujet de TP : Neo4j

S1 2026-2027 - IUT - University de Lille, BUT3 SD

TP Neo4j

Modèle de données Graphe & Initialisation

Le graphe StreamPulse représente les connexions entre Utilisateurs, Artistes, Morceaux (Tracks), Playlists et Genres.

Nœuds (Labels) :

Relations (Types) :


Script d'initialisation (setup_graph.py)

Ce script se connecte à Neo4j, vide la base active et génère un graphe de départ via des requêtes Cypher paramétrées :

from neo4j import GraphDatabase

URI = "neo4j://localhost:7687"
AUTH = ("neo4j", "password")  # À adapter selon vos identifiants

def init_db(tx):
    # Réinitialisation complète du graphe
    tx.run("MATCH (n) DETACH DELETE n")
    
    # Création des contraintes et index de base
    tx.run("CREATE CONSTRAINT IF NOT EXISTS FOR (u:User) REQUIRE u.id IS UNIQUE")
    tx.run("CREATE CONSTRAINT IF NOT EXISTS FOR (a:Artist) REQUIRE a.id IS UNIQUE")
    tx.run("CREATE CONSTRAINT IF NOT EXISTS FOR (t:Track) REQUIRE t.isrc IS UNIQUE")
    tx.run("CREATE CONSTRAINT IF NOT EXISTS FOR (g:Genre) REQUIRE g.name IS UNIQUE")
    
    # Création des Genres
    tx.run("""
    UNWIND ['Electro', 'Synthwave', 'French Touch', 'Rock', 'Indie Pop'] AS g_name
    CREATE (:Genre {name: g_name})
    """)

    # Création des Artistes
    tx.run("""
    CREATE (a1:Artist {id: 'A1', name: 'Daft Punk', country: 'France'}),
           (a2:Artist {id: 'A2', name: 'Kavinsky', country: 'France'}),
           (a3:Artist {id: 'A3', name: 'Justice', country: 'France'}),
           (a4:Artist {id: 'A4', name: 'The Weeknd', country: 'Canada'}),
           (a5:Artist {id: 'A5', name: 'Phoenix', country: 'France'})
    """)

    # Collaborations entre Artistes
    tx.run("""
    MATCH (a1:Artist {id: 'A1'}), (a3:Artist {id: 'A3'}), (a4:Artist {id: 'A4'}), (a2:Artist {id: 'A2'})
    CREATE (a1)-[:COLLABORATED_WITH {count: 3, first_year: 2016}]->(a4),
           (a1)-[:COLLABORATED_WITH {count: 2, first_year: 2007}]->(a3),
           (a2)-[:COLLABORATED_WITH {count: 1, first_year: 2013}]->(a4)
    """)

    # Morceaux et genres
    tx.run("""
    MATCH (g_e:Genre {name: 'Electro'}), (g_s:Genre {name: 'Synthwave'}), (g_ft:Genre {name: 'French Touch'})
    MATCH (a1:Artist {id: 'A1'}), (a2:Artist {id: 'A2'}), (a4:Artist {id: 'A4'})
    
    CREATE (t1:Track {isrc: 'TR001', title: 'Starboy', year: 2016, duration_sec: 230}),
           (t2:Track {isrc: 'TR002', title: 'Nightcall', year: 2010, duration_sec: 259}),
           (t3:Track {isrc: 'TR003', title: 'One More Time', year: 2000, duration_sec: 320})
    
    CREATE (a1)-[:PERFORMED]->(t1), (a4)-[:PERFORMED]->(t1),
           (a2)-[:PERFORMED]->(t2),
           (a1)-[:PERFORMED]->(t3),
           
           (t1)-[:IN_GENRE]->(g_e), (t1)-[:IN_GENRE]->(g_s),
           (t2)-[:IN_GENRE]->(g_s),
           (t3)-[:IN_GENRE]->(g_ft), (t3)-[:IN_GENRE]->(g_e)
    """)

    # Utilisateurs, suivis et écoutes
    tx.run("""
    CREATE (u1:User {id: 'U1', name: 'Alice', country: 'France', premium: true}),
           (u2:User {id: 'U2', name: 'Bob', country: 'France', premium: false}),
           (u3:User {id: 'U3', name: 'Charlie', country: 'USA', premium: true}),
           (u4:User {id: 'U4', name: 'David', country: 'Germany', premium: false})
           
    WITH u1, u2, u3, u4
    MATCH (t1:Track {isrc: 'TR001'}), (t2:Track {isrc: 'TR002'}), (t3:Track {isrc: 'TR003'})
    
    CREATE (u1)-[:FOLLOWS {since_year: 2021}]->(u2),
           (u2)-[:FOLLOWS {since_year: 2022}]->(u3),
           (u1)-[:FOLLOWS {since_year: 2023}]->(u3),
           (u3)-[:FOLLOWS {since_year: 2020}]->(u4),
           
           (u1)-[:LISTENED_TO {play_count: 45, last_played: '2024-01-10'}]->(t1),
           (u1)-[:LISTENED_TO {play_count: 12, last_played: '2024-01-12'}]->(t2),
           (u2)-[:LISTENED_TO {play_count: 88, last_played: '2024-01-15'}]->(t2),
           (u3)-[:LISTENED_TO {play_count: 30, last_played: '2024-01-01'}]->(t1),
           (u3)-[:LISTENED_TO {play_count: 100, last_played: '2024-01-14'}]->(t3)
    """)

with GraphDatabase.driver(URI, auth=AUTH) as driver:
    with driver.session() as session:
        session.execute_write(init_db)
        print("Graphe StreamPulse initialisé avec succès !")

Partie 1 : Connexion, Inspection du Graphe et Prédicats simples (~1h30)

Documentation

Exercices

  1. Connexion & Vérification : Exécute setup_graph.py pour initialiser la base. Crée un script d'inspection qui vérifie la connectivité et affiche le nom de la base de données courante.
  2. Volumétrie des Nœuds : Écris une fonction Python qui compte et affiche le nombre total de nœuds pour chaque label (User, Artist, Track, Genre).
  3. Volumétrie des Relations : Affiche le nombre total de relations de type LISTENED_TO et COLLABORATED_WITH.
  4. Comptage Filtré : Compte combien d'utilisateurs possèdent un compte premium: true. (Indice : Plusieurs syntaxes Cypher sont possibles, soit via des clauses WHERE soit via le filtrage direct dans le pattern).

Partie 2 : Lecture de motifs, Traversées et Projections (~2h)

Documentation

Exercices

  1. Traversée simple : Récupère tous les morceaux exécutés par l'artiste "Daft Punk". Affiche le titre du morceau et son année de sortie.
  2. Profil d'écoute : Liste les artistes écourtés par l'utilisateur "Alice", ainsi que le nombre d'écoutes (play_count) associé à chaque morceau. (Motif : User -> Track <- Artist).
  3. Exploration de relations optionnelles : Pour TOUS les artistes du graphe, affiche leur nom et les titres qu'ils ont interprétés. Si un artiste n'a interprété aucun morceau enregistrés dans la base (ex: "Phoenix"), son nom doit tout de même apparaître avec la mention None.
  4. Chemins de collaboration (Sens & Non-orienté) :
  1. Pagination & Tri : Liste les 2 pistes les plus écoutées par l'ensemble des utilisateurs (somme des play_count), triées par ordre décroissant.

Partie 3 : Écriture, Modulations de structure et Idempotence (CREATE vs MERGE) (~2h30)

Documentation

Exercices

  1. Création simple (CREATE) : Crée un nouvel utilisateur "Eva" (id: 'U5', country: 'France', premium: true).
  2. Création idempotent (MERGE) :
  1. Création de relation conditionnelle : Fais en sorte que l'utilisateur "Eva" suive "Alice" depuis l'année 2024. Si la relation existe déjà, mets seulement à jour le champ since_year sans recréer la relation.
  2. Mise à jour de propriétés d'écoute : L'utilisateur "Alice" réécoute la piste "TR002" (Nightcall). Incrémente son play_count de 1 et mets à jour la propriété last_played avec la date du jour ('2026-08-05').
  3. Restructuration / Suppression : L'utilisateur "David" décide de supprimer son compte. Supprime l'utilisateur U4 ainsi que toutes ses relations sortantes et entrantes du graphe.

Partie 4 : Agrégations, Fonctions d'assemblage et Algorithmes de Graphe en Cypher (~2h30)

Documentation

Exercices

  1. Agrégation globale : Calcule pour chaque utilisateur le nombre total de morceaux écoutés et le volume total d'écoutes (sum(play_count)).
  2. Aggregation avec collections (collect) : Pour chaque artiste, retourne son nom ainsi que la liste (tableau) de tous les titres de ses morceaux.
  3. Degré de réseau (In-degree / Out-degree) : Écris une requête Cypher qui calcule pour chaque utilisateur son nombre de followers (abonnés) et son nombre de followings (abonnements). Identifie l'utilisateur le plus influent du réseau.
  4. Diversité musicale par utilisateur : Calcule le nombre de genres distincts écourtés par chaque utilisateur. (Indice : Nécessite une traversée User -> LISTENED_TO -> Track -> IN_GENRE suivie d'une agrégation appropriée).
  5. Filtrage post-agrégation avec WITH : Écris une requête utilisant WITH qui calcule le total d'écoutes par genre musical, puis ne conserve dans le résultat que les genres comptabilisant au moins 50 écoutes au total.
  6. Décomposition de collections (UNWIND) : À partir d'une liste Python de nouveaux tags distribuée ainsi : ['Indie', 'Alternative', 'Lo-Fi'], utilise UNWIND pour rattacher ces 3 genres d'un seul coup au morceau "TR002".

Partie 5 : Recommandation & Cypher Avancé (~2h)

Documentation

Exercices

Partie A : Moteur de Recommandation

  1. Recommandation basée sur le réseau social ("Ce que vos amis écoutent") :
  1. Filtrage Collaboratif Item-Item ("Les utilisateurs qui ont écouté X ont aussi écouté...") :

Partie B : Performance & Diagnostics

  1. Analyse de plan d'exécution (PROFILE) :
  1. Création d'index :