tech · 5 min de lecture

Exploiter les données d'HopWork avec Neo4j (timeoff 2nd journée)

Début de l'exploration des données d'HopWork avec Neo4j, base de données orientée graphe

Hugo
Hugo19 mars 2014 · 5 min
Exploiter les données d'HopWork avec Neo4j (timeoff 2nd journée)

Second jour de timeoff Lateral-Thoughts, je tente toujours de me tenir à un billet par jour. Et aujourd’hui ce post concernera Neo4J.

Neo4j fait partie des bases de données orientée graphe. Chez LT nous avons la chance d’avoir Florent Biville qui maîtrise bien le sujet et j’ai un peu profité de lui, en tout bien tout honneur, pour me faire un peu guider dessus.

Une base de données graphe par définition c’est une base qui traite de relation. Eh oui, toutes les bases nosql n’ont pas abandonné la composante relationnelle. Et c’est bien ce qui m’intéresse ici car je veux jouer avec les profils inscrits sur HopWork en les reliant via leurs compétences.

Objectif pour moi, répondre à des questions simples :

  • les mots clés les plus utilisés. Cette requête sert pour la mise en jambe car elle ne profite pas des relations
  • les mots clés les plus souvent associés
  • chercher un profil “proche” vis à vis d’un autre en prenant en compte uniquement les mots clés

Le chargement

Première étape, on va charger les données à partir de MongoDB. Pour cela on va utiliser un simple main Java :

java

       GraphDatabaseService graphDb = new GraphDatabaseFactory().newEmbeddedDatabase( "C:/temp/graphdb" );
       ExecutionEngine executionEngine = new ExecutionEngine(graphDb);

       MongoURI mongoURI = new MongoURI("mongodb://localhost/workable");
       DB db = mongoURI.connectDB();
       Jongo jongo = new Jongo(db);
       MongoCollection profiles = jongo.getCollection("profiles");

       profiles.find().as(Profile.class).forEach(profile -> {
            Map<String, Object> params = new HashMap<>();
            params.put("id", profile.getKey().toString());
           executionEngine.execute("MERGE (p:PROFILE {key: {id}}) ON CREATE set p.created  = 'true' ", params);

           profile.getInfos().getKeyWords().forEach(keyword -> {
               params.put("keyword", keyword);
               executionEngine.execute("MERGE (k:KEYWORD {label: {keyword} }) ON CREATE set k.created  = 'true' ", params);
               ExecutionResult relationResult = executionEngine.execute("MATCH (p:PROFILE {key: {id} }), (k:KEYWORD {label: {keyword} }) " +
                       "CREATE UNIQUE p-\[:SPECIALISES\_IN\]-> k", params);
           });
       });
       graphDb.shutdown();

Ici on utilise un executionEngine pour exécuter des requêtes CYPHER, le langage de requêtage pour Neo4J.

On utilise Jongo pour interroger la base MongoDB.

On remarquera l’utilisation de Java 8 pour itérer sur les collections, faut bien fêter la sortie de Java8 :)

Les requêtes CYPHER utilisées pour insérer les noeuds profiles et les noeuds keywords ont pour but d’éviter d’insérer des doublons :

text
MERGE (k:KEYWORD {label: {keyword} }) ON CREATE set k.created  = 'true'

La requête pour insérer les relations entre keywords et profiles utilise CREATE UNIQUE pour éviter d’ajouter une relation déjà existante :

text
MATCH (p:PROFILE {key: {id} }), (k:KEYWORD {label: {keyword} })
CREATE UNIQUE p-\[:SPECIALISES\_IN\]->k

Ensuite il suffit d’utiliser la console Neo4j pour afficher le résultat :

graph
graph

Quelques requêtes

Désormais nous avons nos données, essayons quelques requêtes.

Pour exécuter ces requêtes, la console Neo4j va rapidement montrer ces limites. J’ai souhaité utiliser une autre console un peu plus sympathique, rabbithole. Malheureusement je n’ai pas réussi à la démarrer pour des erreurs cryptiques en tant que débutant. Je suis donc reparti sur une exécution en Java.

Quels sont les mots clés les plus utilisés :

text
MATCH (:PROFILE)-\[s:SPECIALISES\_IN\]->(k:KEYWORD)

RETURN k, count(s) AS relCount

ORDER BY relCount DESC

LIMIT 10;

Le résultat sur les données de production HopWork:

+--------------------------------------------------------+ | k | relCount | +--------------------------------------------------------+ | Node[67]{created:"true",label:"web"} | 183 | | Node[403]{created:"true",label:"php"} | 148 | | Node[58]{created:"true",label:"webdesign"} | 145 | | Node[39]{created:"true",label:"javascript"} | 127 | | Node[451]{created:"true",label:"wordpress"} | 122 | | Node[50]{created:"true",label:"logo"} | 107 | | Node[120]{created:"true",label:"css"} | 103 | | Node[18]{created:"true",label:"graphiste"} | 101 | | Node[125]{created:"true",label:"PHP"} | 100 | | Node[1]{created:"true",label:"Java"} | 100 | +--------------------------------------------------------+

On remarque évidemment tout de suite que PHP et php sont comptées différemment. Bon, je m’en doutais lors de l’insertion, il y a une grosse phase de normalisation à faire sur les mots clés. Déjà il aurait fallu s’occuper des différences de casses, mais aussi des synonymes (J2EE JEE) ou des regroupements (php relié à php5, php6 etc…).

Tant pis pour l’instant.

Quels sont les couples de mots clés les plus souvent associés :

text
MATCH (k2:KEYWORD)<-\[:SPECIALISES\_IN\]-(:PROFILE)-\[:SPECIALISES\_IN\]->(k1:KEYWORD)
WITH CASE k1.label > k2.label
WHEN true THEN k1.label + '\_' + k2.label
ELSE k2.label + '\_' + k1.label
END AS result
RETURN DISTINCT(result), COUNT(result)/2 AS count
ORDER BY count DESC
LIMIT 10;

Bon là clairement c’est une requête très peu optimisé et le temps d’exécution sur les données d’HopWork est… infini. Je vais faire appel à un ami (Florent) pour l’améliorer :)

Et la dernière requête, partant de mon profil, qui est le plus proche de moi sur HopWork en comptant le nombre de mots clés en commun (faite par Florent) :

text
MATCH (currentProfile:PROFILE { id:123 })-\[:SPECIALISES\_IN\]->(k:KEYWORD)<-\[:SPECIALISES\_IN\]-(otherProfile:PROFILE)
RETURN otherProfile, collect(k), COUNT(k) AS keywordCount
ORDER BY keywordCount DESC LIMIT 6;

+-----------------------------------------------------------------------------------------------------------------------------------+

| otherProfile | collect(k.label) | keywordCount |

+-----------------------------------------------------------------------------------------------------------------------------------+

| Node[4205]{key:"Raphael",created:"true"} | ["javascript","mongodb","elasticsearch","performance"] | 4 |

| Node[3302]{key:"Sylvain",created:"true"} | ["javascript","elasticsearch","nosql","mongodb"] | 4 |

| Node[6351]{key:"Katia",created:"true"} | ["nosql","javascript","mongodb"] | 3 |

| Node[3265]{key:"Zahir",created:"true"} | ["mongodb","backbone","javascript"] | 3 |

| Node[3452]{key:"Maxence",created:"true"} | ["elasticsearch","mongodb","javascript"] | 3 |

| Node[4869]{key:"Arnault",created:"true"} | ["mongodb","elasticsearch","performance"] | 3 |

+-----------------------------------------------------------------------------------------------------------------------------------+

(J'ai remplacé les id par les liens vers les profils HopWork pour les besoins du billet)

Avis au bout d’une journée

Dans l’ensemble, on voit un peu comment pourrait se dessiner un moteur de recommandation basé sur un graphe des données dans HopWork. L’outil est intéressant, un peu raide à prendre en main car les infos restent assez parcellaires sur le net. Je prends juste l’exemple des paramètres nommés dans une requête CYPHER, rien dans la doc n’explique le format attendu.

Je suis bien conscient que je suis resté très en surface mais l’outil et ses applications font que j’y retournerais sans doute très prochainement.

Hugo

Écrit par

Hugo

Software Engineer with more than 20 years of experience. I love to share about technologies and startups

Vous avez aimé ce récit ?

Environ un mail par mois, quand un texte mérite d'être écrit. Vos lecteurs restent vos lecteurs.

205 abonnés · désabonnement en un clic

À lire aussi

On devrait toujours travailler comme ça (bis)

Retour sur un timeoff Lateral-Thoughts

8 déc. 2013
On devrait toujours travailler comme ça (bis)

Hopwork is coming

Peut être que vous l'avez déjà lu sur Twitter, Hopwork vient de passer un nouveau cap. En effet je suis plutôt heureux d'annoncer que nous venons de f...

16 déc. 2016
Hopwork is coming

Nosql, 4 bases à la loupe 4/4

Retour d'expérience sur Redis, MongoDb, Solr, Elastic Search pour de la gestion de logs

11 nov. 2012
Nosql, 4 bases à la loupe 4/4

Réponses

0 Commentaire

Pas encore de commentaire. Soyez le premier !