Skip to content

Instantly share code, notes, and snippets.

@MPiunti
Last active March 2, 2016 14:33
Show Gist options
  • Save MPiunti/d118d26c3a101bef9b52 to your computer and use it in GitHub Desktop.
Save MPiunti/d118d26c3a101bef9b52 to your computer and use it in GitHub Desktop.
image::https://dl.dropboxusercontent.com/u/2236831/logo_jobme_neg.png[jOB.me,150]
Part 1 - link:./?9765844[Building the Graph]
== Extending the Graph ==
Modeled and developed in http://www.neo4j.org/[*neo4j.org*], the graph here is extendes to include *Events, Technologies, Area of Interests, Contents*.
Nodes are related each other using lalelled relationships.
//hide
//setup
//output
[source,cypher]
----
CREATE
(p1:Person {id:"1", gender:"M", name:"Jean Paul", City:"Paris"}),
(p2:Person {id:"2", gender:"M", name:"John", City:"London"}),
(p3:Person {id:"3", gender:"M", name:"Karl", City:"Detroit"}),
(c1:Company {id:"4", name:"Reply Inc.", City:"Detroit"}),
(c2:Company {id:"5", name:"Bitmama", City:"London"}),
(c3:Company {id:"6", name:"Spike Reply", City:"Dussendorf"}),
(cc1:Country {id:"7", name:"US"}),
(cc2:Country {id:"8", name:"UK"}),
(cc3:Country {id:"9", name:"DE"}),
(ev1:Event {type:"Labcamp", name:"Neo4j Graph DB"}),
(t1:Technology {name:"Neo4j"}),
(t2:Technology {name:"OrientDB"}),
(t3:Technology {name:"TITAN Aurelius"}),
(a1:Area {name:"Graph+DB"}),
(cc1)-[:HAS]->(c1),
(cc2)-[:HAS]->(c2),
(cc3)-[:HAS]->(c3),
(p1)-[:WORKS]->(c1),
(p2)-[:WORKS]->(c2),
(p3)-[:WORKS]->(c3),
(st1:Skill {rank:"1", name:"Agent+Oriented+Programming+and+Modeling"}),
(st2:Skill {rank:"1", name:"Artificial+Intelligence"}),
(st3:Skill {rank:"2", name:"Big+Data"}),
(st4:Skill {rank:"3", name:"Graph+DB"}),
(st5:Skill {rank:"5", name:"Information+design"}),
(st6:Skill {rank:"8", name:"j2ee"}),
(st7:Skill {rank:"13", name:"java"}),
(st8:Skill {rank:"21", name:"Javascript"}),
(st9:Skill {rank:"34", name:"JQuery"}),
(st10:Skill {rank:"55", name:"jQuery+Mobile"}),
(st11:Skill {rank:"89", name:"Neo4j"}),
(st12:Skill {rank:"144", name:"Open+data"}),
(st13:Skill {rank:"233", name:"Oracle"}),
(st14:Skill {rank:"377", name:"PL-SQL"}),
(st15:Skill {rank:"610", name:"REST"}),
(st16:Skill {rank:"987", name:"Social+Media"}),
(st17:Skill {rank:"1597", name:"Spring"}),
(st18:Skill {rank:"2584", name:"Spring+framework"}),
(st19:Skill {rank:"4181", name:"SQL"}),
(st20:Skill {rank:"6765", name:"Struts"}),
(st21:Skill {rank:"10946", name:"Web+2.0"}),
(st22:Skill {rank:"17711", name:"Web+Application"}),
(st23:Skill {rank:"28657", name:"apache"}),
(st24:Skill {rank:"46386", name:"apple"}),
(st25:Skill {rank:"75025", name:"Hadoop"}),
(st26:Skill {rank:"75025", name:"J2EE+developer"}),
(st27:Skill {rank:"75026", name:"jqueryUI"}),
(st28:Skill {rank:"75027", name:"mongodb"}),
(st29:Skill {rank:"7503", name:"prototype+javascript"}),
(st30:Skill {rank:"75028", name:"swat"}),
(st31:Skill {rank:"755", name:"tamtamy"}),
(st32:Skill {rank:"7245", name:"tomcat"}),
(st33:Skill {rank:"25", name:"Nosql"}),
(st34:Skill {rank:"55", name:"angularjs"}),
(st35:Skill {rank:"9535", name:"CSS"}),
(st36:Skill {rank:"7535", name:"d3js"}),
(st37:Skill {rank:"7535", name:"Cypher"}),
(co1:Content {title:"Bootstrap 3 Released", source:"infoQ"}),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st1),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st2),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st3),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st4),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st5),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st6),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st7),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st8),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st9),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st10),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st11),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st12),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st13),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st14),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st15),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st16),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st17),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st18),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st19),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st20),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st21),
(p1)-[:HAS_SKILL {source:"GitHub"}]->(st37),
(p1)-[:HAS_SKILL {source:"TamTamy"}]->(st22),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st23),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st24),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st25),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st26),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st27),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st28),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st29),
(p2)-[:HAS_SKILL {source:"GitHub"}]->(st30),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st31),
(p2)-[:HAS_SKILL {source:"StackOverflow"}]->(st32),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st33),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st3),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st4),
(p2)-[:HAS_SKILL {source:"StackOverflow"}]->(st6),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st7),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st8),
(p2)-[:HAS_SKILL {source:"GitHub"}]->(st8),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st9),
(p2)-[:HAS_SKILL {source:"StackOverflow"}]->(st10),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st11),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st13),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st14),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st17),
(p2)-[:HAS_SKILL {source:"StackOverflow"}]->(st19),
(p2)-[:HAS_SKILL {source:"TamTamy"}]->(st35),
(p2)-[:SHARED]->(co1),
(p3)-[:HAS_SKILL {source:"TamTamy"}]->(st8),
(p3)-[:HAS_SKILL {source:"TamTamy"}]->(st34),
(p3)-[:HAS_SKILL {source:"TamTamy"}]->(st35),
(p3)-[:HAS_SKILL {source:"TamTamy"}]->(st28),
(p3)-[:HAS_SKILL {source:"TamTamy"}]->(st11),
(p3)-[:HAS_SKILL {source:"TamTamy"}]->(st13),
(p3)-[:HAS_SKILL {source:"TamTamy"}]->(st14),
(p3)-[:HAS_SKILL {source:"TamTamy"}]->(st18),
(p3)-[:HAS_SKILL {source:"TamTamy"}]->(st35),
(p3)-[:LIKED]->(co1),
(v1:Vacancy {id:"10", type:"Season", name:"Summer Vacancy", City:"Belo Horizonte"}),
(c4:Company {id:"11", name:"Reply Brasil", City:"Belo Horizonte"}),
(v1)-[:RAISED_BY]->(c4),
(v1)-[:REQUIRES]->(st3),
(v1)-[:REQUIRES]->(st4),
(v1)-[:REQUIRES]->(st6),
(v1)-[:REQUIRES]->(st8),
(v1)-[:REQUIRES]->(st11),
(v1)-[:REQUIRES]->(st15),
(v1)-[:REQUIRES]->(st25),
(v1)-[:REQUIRES]->(st28),
(v1)-[:REQUIRES]->(st33),
(v1)-[:REQUIRES]->(st34),
(v1)-[:REQUIRES]->(st35),
(v2:Vacancy {id:"11", type:"Season", name:"Stage", City:"Chicago"}),
(c5:Company {id:"12", name:"Reply Inc.", City:"Chicago"}),
(v2)-[:RAISED_BY]->(c5),
(v2)-[:REQUIRES]->(st8),
(v2)-[:REQUIRES]->(st18),
(v2)-[:REQUIRES]->(st20),
(v2)-[:REQUIRES]->(st21),
(v2)-[:REQUIRES]->(st22),
(v2)-[:REQUIRES]->(st23),
(v2)-[:REQUIRES]->(st26),
(v2)-[:REQUIRES]->(st27),
(v2)-[:REQUIRES]->(st29),
(v2)-[:REQUIRES]->(st32),
(v2)-[:REQUIRES]->(st35),
(v2)-[:REQUIRES]->(st34),
(v3:Vacancy {id:"13", type:"Season", name:"Temporary Position", City:"London"}),
(c6:Company {id:"14", name:"Glue Reply", City:"London"}),
(v3)-[:RAISED_BY]->(c6),
(v3)-[:REQUIRES]->(st3),
(v3)-[:REQUIRES]->(st6),
(v3)-[:REQUIRES]->(st7),
(v3)-[:REQUIRES]->(st12),
(v3)-[:REQUIRES]->(st15),
(v3)-[:REQUIRES]->(st23),
(v3)-[:REQUIRES]->(st32),
// LEVEL2
(p2)-[:ATTENDED]->(ev1),
(p3)-[:ATTENDED]->(ev1),
(ev1)-[:ADDRESSED]->(t1),
(t1)-[:INCLUDES]->(st37),
(t1)-[:INCLUDES]->(st6),
(t1)-[:INCLUDES]->(st15),
(a1)-[:INVOLVES]->(t1),
(a1)-[:INVOLVES]->(t2),
(a1)-[:INVOLVES]->(t3),
(p2)-[:INTERESTED {source:"Inferenced"}]->(a1),
(p3)-[:INTERESTED {source:"Inferenced"}]->(a1)
----
Which gives the following Graph
//graph
The following query shows who exhibits the same skills among users:
[source,cypher]
----
MATCH (p1{name:'Karl'})-[:HAS_SKILL]->(Skill),
(p2{name:'John'})-[:HAS_SKILL]->(Skill)
RETURN p1.name,p2.name, Skill.name
----
which, for the provided graph, gives the following result:
//table
And the following shows all the users who shared (or liked) a given content on the time line:
[source,cypher]
----
MATCH (m)-[:SHARED|LIKED]->(Content)
RETURN m.name,Content.title
----
//table
=== Third Party Sources ===
The graph is in this case fed with external sources, through the use of public APIs. For instance, users skill cloud is extendend connecting http://stackoverflow.com/[*Stackoverflow*] and https://github.com/[*GitHub*].
[source,cypher]
----
MATCH (person{name:'John'})-[r:HAS_SKILL]->(skill)
RETURN person.name,r.source,skill.name
----
//table
The following query unveils _who knows_ a given technolgy, ranking the results accoridng to the whole set of external information sources:
[source,cypher]
----
MATCH (p:Person)-[:HAS_SKILL]->(s:Skill),
(t:Technology)-[:INCLUDES]->(s)
RETURN p.name AS name, t.name,
count(s) AS Skill_o_Meter,
collect(s.name) AS skills
----
//table
=== Reccomendation Engine ===
Once anyone partecipates an event, we may suggest area of interests and related technologies:
[source,cypher]
----
MATCH (p2{name:'John'})-[:ATTENDED]->(Event),
(Event)-[:ADDRESSED]->(Technology),
(Technology)<-[:INVOLVES]-(Area),
(Area)-[:INVOLVES]->(Suggestions)
RETURN Area.name, Suggestions.name
----
//table
*Step 1: Generate concrete relationships on hidden connections* Adopting a typical argumentation mechanisms, the following script *generates* brand new realtionships of type [:SUPPORTS] every time a content is found: these supports links toghether the content node and the skills owned by the publisher user and any of the users who "LIKED" this content.
[source,cypher]
----
MATCH (content:Content) <-[:SHARED|LIKED]-(person:Person),
(person)-[:HAS_SKILL]->(skill:Skill)
WITH content, skill, count(skill) as weight
CREATE p=(content)-[r:SUPPORTS {weight: weight}]-> (skill)
RETURN content.title, skill.name, r.weight as SUPPORTS
ORDER BY r.weight DESC
----
The resulting sub-graph constitutes a 'digest' for the skills which can be related to this content. New [:SUPPORTS] are automatically generated between contents and skills.
//table
*Step 2: Generate User profiled Recommendation* the meta inforation introduced with the previous argumentation phase can be then applied to _suggest_ content to the users exhibiting the same skill configuration:
[source,cypher]
----
MATCH (person)-[:HAS_SKILL]->(skill),
(content)-[r:SUPPORTS]-> (skill)
WHERE NOT (person)-[:SHARED|LIKED]->(content)
RETURN person.name, content.title, skill.name, r.weight
ORDER BY r.weight DESC
----
//table
The results provide a *Social Reccomendation*. It can be noticed how the _weight_ is tuned with the supports which the content skills have received in the previous argumentation phase.
Similarly, given a user profile we may find any possible content recommendation based on his interest and skills. The following Cypher query can be executed for retrieving such an information.
[source,cypher]
----
MATCH (person:Person)-[:HAS_SKILL]->(skill:Skill),
(skill) <-[r:SUPPORTS]-(content:Content)
WHERE NOT (person)-[:SHARED|LIKED]->(content)
RETURN person.name , content.title as title, sum(r.weight) as weight
ORDER BY weight DESC
----
The retrieved result can be then used as a reccomendation to the matching users, as follows:
//table
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment