Lets use docker to help us set up an environment to run the graph database in!
Add the recipe nodes into neo4j
python intial_recipes.pyAdd all the other info into neo4j
python complete_recipes.pyNow we can build our recommendation system!
Lets begin with a simple filter we have a few friends coming over one vegan and one is allergic to nuts. We also don't want to spend more than 20-25 mins cooking!
MATCH (r:Recipe)-[rel:DIET_TYPE]->(d:DietType)
WHERE r.skillLevel = 'Easy'
AND r.preparationTime > 1200
AND r.preparationTime < 1800
AND d.name = 'Vegan' OR d.name = 'Nut-free'
RETURN r, d, relLets use what we have in our fridge!
MATCH (r:Recipe)-[rel:CONTAINS_INGREDIENT]->(i:Ingredient)
WHERE i.name IN ['double cream', 'caramel']
WITH r, COLLECT(i) AS ingredients, COLLECT(rel) AS relationships
WHERE SIZE(ingredients) = 2
RETURN r, ingredients, relationshipsWe really like 'Pull-apart chicken with green curry & lime leaf dressing' how can we find similar recipes?
WITH "Pull-apart chicken with green curry & lime leaf dressing" AS favoriteRecipeName
MATCH (fav:Recipe {name: favoriteRecipeName})
OPTIONAL MATCH (fav)-[:KEYWORD]->(k:Keyword)
OPTIONAL MATCH (fav)-[:CONTAINS_INGREDIENT]->(i:Ingredient)
OPTIONAL MATCH (fav)-[:COLLECTION]->(favCollection:Collection)
OPTIONAL MATCH (fav)-[:DIET_TYPE]->(d:DietType)
MATCH (rec:Recipe)-[:KEYWORD]->(k)
WHERE rec <> fav
WITH fav, i, favCollection, d, rec, COUNT(k) AS sharedKeywords
MATCH (rec)-[:CONTAINS_INGREDIENT]->(i)
WHERE rec <> fav
WITH fav, favCollection, d, rec, sharedKeywords, COUNT(i) AS sharedIngredients
WHERE NOT (rec)-[:COLLECTION]->(favCollection)
RETURN rec.name AS RecommendedRecipe, sharedKeywords, sharedIngredients
ORDER BY sharedKeywords DESC, sharedIngredients DESC
LIMIT 10WITH "Pull-apart chicken with green curry & lime leaf dressing" AS favoriteRecipeName
MATCH (fav:Recipe {name: favoriteRecipeName})
OPTIONAL MATCH (fav)-[:CONTAINS_INGREDIENT]->(i:Ingredient)
OPTIONAL MATCH (fav)-[:KEYWORD]->(k:Keyword)
OPTIONAL MATCH (fav)<-[:WROTE]-(a:Author)
WITH fav, i, k, a, COUNT(a) AS authorInfluence
ORDER BY authorInfluence DESC
LIMIT 10
MATCH (rec:Recipe)-[:CONTAINS_INGREDIENT]->(i), (rec)-[:KEYWORD]->(k), (rec)<-[:WROTE]-(a)
WHERE rec <> fav
WITH rec, COUNT(i) + COUNT(k) AS relevanceScore
ORDER BY relevanceScore DESC
LIMIT 10
RETURN rec.name AS RecommendedRecipe, relevanceScoreThese look similar which is great but what about if we want to leverage the graph to find things related that are not too similar!
WITH "Pull-apart chicken with green curry & lime leaf dressing" AS favoriteRecipeName
MATCH (fav:Recipe {name: favoriteRecipeName})
MATCH (fav)-[:CONTAINS_INGREDIENT|KEYWORD|WROTE*4]-(rec:Recipe)-[:DIET_TYPE]->(d:DietType {name: "Vegan"})
WHERE fav <> rec
WITH rec, COUNT(*) AS pathsOfLength4
ORDER BY pathsOfLength4 DESC
LIMIT 5
RETURN rec.name AS RecommendedRecipe, pathsOfLength4Super intresting results if you want to dig deeper you can being here (https://neo4j.com/docs/getting-started/appendix/tutorials/guide-build-a-recommendation-engine/) and keep digging!