ALEXANDRE LOUVET


Sujet de TP : Neo4j

S1 2026-2027 - IUT - Université 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

# Configuration de la connexion
URI = "neo4j://localhost:7687"
AUTH = ("neo4j", "password")  # À adapter selon vos identifiants Neo4j


# Reinitialisation
def reset_database(driver):
    with driver.session() as session:
        def _clear_db(tx):
            tx.run("MATCH (n) DETACH DELETE n")

        session.execute_write(_clear_db)

        def _populate_db(tx):
            tx.run("""
            UNWIND ['Electro', 'Synthwave', 'French Touch', 'Rock', 'Indie Pop'] AS g_name
            CREATE (:Genre {name: g_name})
            """)

            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'})
            """)

            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),
                   (a4)-[:COLLABORATED_WITH {count: 1, first_year: 2013}]->(a2)
            """)

            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)
            """)

            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)
            """)

        session.execute_write(_populate_db)

with GraphDatabase.driver(URI, auth=AUTH) as driver:
    reset_database(driver)

Partie 1 : Connexion, Inspection du Graphe et Prédicats simples

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é.

  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. Utiliser deux méthodes:

    A. Via une clause WHERE.

    B. Via le filtrage direct dans le pattern.


Partie 2 : Lecture de motifs, Traversées et Projections

Documentation

Exercices

  1. Traversée simple : Récupère tous les morceaux joués par l'artiste "Daft Punk". Affiche le titre du morceau et son année de sortie.
  2. Profil d'écoute : Liste les artistes écouté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é) : Trouve les artistes avec lesquels "Daft Punk" a collaboré directement. Modifie ensuite la requête pour trouver les artistes avec lesquels "Daft Punk" a collaboré de manière directe OU indirecte à 2 sauts près (-[:COLLABORATED_WITH*1..2]-).
  5. 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)

Documentation

Exercices

  1. Création simple (CREATE) : Crée un nouvel utilisateur "Eva" (id: 'U5', country: 'France', premium: true).
  2. Création idempotent (MERGE) : Écris une fonction Python qui ajoute un genre musical passé en paramètre. Exécute cette fonction deux fois avec le genre "Synthwave". Vérifie dans la base qu'aucun doublon n'a été créé.
  3. 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.
  4. 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').
  5. 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égation et Fonctions d'assemblage

Documentation

Exercices

  1. Agrégation globale : Calcule pour chaque utilisateur le nombre total de morceaux écoutés et le volume total d'écoutes.
  2. Aggregation avec collections (collect) : Pour chaque artiste, retourne son nom ainsi que la liste 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 écoutés par chaque utilisateur.
  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

Exercices

  1. Recommandation basée sur le réseau social ("Ce que vos amis écoutent") : Écris une requête de recommandation pour l'utilisateur "Alice". La requête doit trouver les morceaux écoutés par les personnes qu'Alice suit (FOLLOWS), mais qu'Alice n'a pas encore écoutés. Affiche le titre du morceau recommandé et le nombre d'amis qui l'ont écouté.

  2. Filtrage Collaboratif Item-Item ("Les utilisateurs qui ont écouté X ont aussi écouté...") : L'utilisateur "Bob" vient d'écouter le morceau "Nightcall" (TR002). Trouve les autres utilisateurs qui ont aussi écouté "Nightcall". Identifie les autres morceaux que ces utilisateurs ont écoutés (exclure ceux que Bob a déjà écoutés). Ordonne les résultats par popularité auprès de ces utilisateurs similaires.