0
votes

Use case

My app should push articles to users (doctors) based on their profile and user actions. In the following example, consider (see also graph below):

  • Two Doctors (one being Profession:Doctor, Specialty:Oncology, the other one being a Doctor, Specialty:unknown)
  • An article has been manually linked to Profession:Doctor, Specialty:Oncology and Keyword:Knee surgery (i.e., show this article to users matching this profile.).

The front end sends a user id to the backend and the backend needs to respond with n articles that match the user profile.

enter image description here

Question

What Neo4j cypher query would basically translate to MATCH (a:Article)... given a user profile. Or said differently find (a:Article) [....] that has similar relationships to Profession, Keyword, and Specialty nodes that the user has.

What shape could & should my cypher query look like ?

1
Do you understand how to find :Articles using only one of :Keyword, :Profession, or :Specialty for a :User?rickhg12hs
I do know how to match basic pathes like MATCH (a:Article)-[]-(n)-[]... etc, but here i want to find the "exact matching" article for a given user given the relationships between both of them (where article has the most relationships of 2nd level with user)Romain Bruckert
Do you have some test data to share?rickhg12hs
Just the data you seen on the screensot. I can export create statements if need be.Romain Bruckert
After I edited your post a little, I upvoted. I think most people like to see a MWE, even if it's a fail. ... or a clear example of a desired output, given an input, etc. A source of some example data also saves a lot of time for solution seekers. Also, the Neo4j Community is a good place to ask for some Cypher code. They like use cases too.rickhg12hs

1 Answers

1
votes

TL;DR

Here's a start:

MATCH p=(u:User {id: $theID})-[:r]->()<-[:m]-(a:Article)
WITH a, COUNT(p) as rank
RETURN a.name, rank
ORDER BY rank DESC
LIMIT $n

The idea here is to just count all the paths from the user to each article and then use that as a rank. The more paths, i.e., the more common specialties and keywords, the higher the rank.

The long version ... More data, "rank" validation

I'm pretty sure your queries will evolve as your graph database grows and you adjust the schema, i.e., you probably will have more relationship types and connections.

To have more data to play with, I generated some fake doctors, articles, etc. I'm pretty sure there's a better way to do this.

MERGE (p:Profession {name: "Doctor"})
WITH p, ["Fred","Wilma","Pebbles","Dino","Barney","Betty","Bamm-bamm", "Hoppy"] as doctors
UNWIND doctors as doc
MERGE (d:User {name: doc})-[:R]->(p);

WITH ["Oncology", "Pathology","Pediatrics","ENT","Radiology","Dermatology"] as specialties
UNWIND specialties as spec
MERGE (:Specialty {name: spec});

MATCH (d:User)
WITH d, ["Oncology", "Pathology","Pediatrics","ENT","Radiology","Dermatology"] as specialties
UNWIND apoc.coll.randomItems(specialties, toInteger(apoc.text.random(1,"00111223"))) as docspec
MATCH (spec:Specialty {name: docspec})
MERGE (d)-[:R]->(spec);

WITH ["Knee Surgery","Ear Injury","Throat Cultures","Radial fractures", "Arm rash", "Mole color"] as keywords
UNWIND keywords as key
MERGE (:Keyword {name: key});

MATCH (d:User)
WITH d, ["Knee Surgery","Ear Injury","Throat Cultures","Radial fractures", "Arm rash", "Mole color"] as keywords
UNWIND apoc.coll.randomItems(keywords, toInteger(apoc.text.random(1,"01112233"))) as docword
MATCH (key:Keyword {name: docword})
MERGE (d)-[:R]->(key);

MATCH (p:Profession {name: "Doctor"})
WITH p, ["Useful article","Cool article","Awesome article","Research article","Nifty article","Health article"] as articles
UNWIND articles as art
MERGE (:Article {name: art})-[:M]->(p);

MATCH (art:Article)
WITH art, ["Oncology", "Pathology","Pediatrics","ENT","Radiology","Dermatology"] as specialties
UNWIND apoc.coll.randomItems(specialties, toInteger(apoc.text.random(1,"111223"))) as artspec
MATCH (spec:Specialty {name: artspec})
MERGE (art)-[:M]->(spec);

MATCH (art:Article)
WITH art, ["Knee Surgery","Ear Injury","Throat Cultures","Radial fractures", "Arm rash", "Mole color"] as keywords
UNWIND apoc.coll.randomItems(keywords, toInteger(apoc.text.random(1,"1112233"))) as artword
MATCH (key:Keyword {name: artword})
MERGE (art)-[:M]->(key);

Here's the intertwined mess.

enter image description here

Now looking at just one "doctor":

MATCH p=(u:User {name: "Barney"})-[:R]->()<-[:M]-(a:Article)
RETURN p

... you can see all the articles he's connected to.

enter image description here

So, let's rank them by the number of connections to the doctor.

MATCH p=(u:User {name: "Barney"})-[:R]->()<-[:M]-(a:Article)
WITH a, COUNT(p) as rank
RETURN a.name, rank
ORDER BY rank DESC

enter image description here

Dr. Barney probably would be interested in "Nifty article".

There are lots of ways to rank, this is a just a simple way.