cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

How to get recommendations based on previous orders in neo4j?

I am trying to work on neo4j for the first time. I have written the following:

LOAD CSV WITH HEADERS FROM "file:///restaurant_data.csv" AS data
MERGE(n1:Customer{Name:data.Name, Latitude:toFloat(data.Latitude),Longitude:toFloat(data.Longitude)})
MERGE(n2:Orders{OrderId:data.Order_ID,OrderTimestamp:data.Order_ts,FoodName:data.Food_Item})
MERGE(n3:Restaurant{RestaurantName:data.Restaurant, RestLat:toFloat(data.Rest_lat), RestLong:toFloat(data.Rest_long)})
MERGE (n1)-[r1:PLACES_ORDER]->(n2)
MERGE (n2)-[r2:BELONGS_TO]->(n3)
MERGE (n3)-[r3:SERVES]->(n2)
RETURN *;

I want to get a Customers last 5 orders by order timestamp desc and then give him recommendations of restaurants serving that food. I also want to give him recommendations based on his location. the distance between him and restaurant should be less than 5kms.

I have written the following query:

MATCH(n1:Customer{Name:"Angy"})
MATCH(n1)-[:PLACES_ORDER]->(n2:Orders)<-[:SERVES]-(rec:Restaurant)
RETURN n2.FoodName, n2.OrderTimestamp, n3.RestaurantName
ORDER BY n2.OrderTimestamp
LIMIT 5

which successfully gave me last 5 orders.

then I wrote this one however it gives me "no matches, no records"

MATCH(n1:Customer{Name:"Angy"})-[:PLACES_ORDER]->(n2:Orders)<-[:SERVES]-(:Restaurant)
WITH n2 ORDER BY n2.OrderTimestamp DESC LIMIT 5
WITH collect(distinct n2) as orders
MATCH (r:Restaurant) WHERE ALL(order in orders WHERE EXISTS((r)-[:SERVES]->(order))) 
RETURN distinct r.RestaurantName 

How do I achieve what I want? I have been spending a lot of hours but unable to get anything. Please help.

11 REPLIES 11

This should work:

MATCH(n1:Customer{Name:"Angy"})-[:PLACES_ORDER]->(n2:Orders)
WITH n2 ORDER BY n2.OrderTimestamp DESC LIMIT 5
MATCH (n2)<-[:SERVES]-(:Restaurant)
RETURN distinct r.RestaurantName

You may want to consider a different domain model. Instead of storying the food information in the order, create a Item node for each Food item. Then link the order to the food item, which is linked to the restaurant.

(:Customer)-[:PLACES_ORDER]->(:Order)-[:CONTAINS_ITEM]->(:Item)<-[:SERVES]-(:Restaurant)

This model allows an order to have multiple items and an item can be served by multiple restaurants.

Hi This is working but this is not what I want. From your query I got restaurants who served Angy. I want to get restuarants which have those food items which Angy has ordered so that Angy. basically reccomendations for Angy. How to get that?

Ok, you need to get the foods from the person's last five orders, and look for restaurants that serve those foods. Try the following:

MATCH(:Customer{Name:"Angy"})-[:PLACES_ORDER]->(o:Orders)
WITH o ORDER BY o.OrderTimestamp DESC LIMIT 5
WITH [o.FoodName] as foods
MATCH (:Restaurant)-[:SERVES]->(n:Orders)
WHERE (n.FoodName in foods)
RETURN distinct r.RestaurantName

You may want to consider the following data model as an alternative:
(:Customer)-[:PLACES_ORDER]->(:Orders)-[:CONTAINS_ITEM]->(:Item)<-[:SERVES]-(:Restaurant)

In this case, the query would look something like:
MATCH(:Customer{Name:"Angy"})-[:PLACES_ORDER]->(o:Orders)
WITH o ORDER BY o.OrderTimestamp DESC LIMIT 5
MATCH (o)-[:CONTAINS_ITEM]->(:Item)<-[:SERVES]-(r:Restaurant)
RETURN DISTINCT r.RestaurantName

Ok thank you so much this is working. I am also going to try the other data model you suggest. Can you also suggest how can I calculate Location based on my Lat and Long coordinates and choose restaurants which are nearest to customer's Lat and Long?

I am thinking something like this, where $myLongitude and $myLatitude are parameters passed to the query. I don't have experience using the point/distance methods, but I assume they work as advertised. I don't have test data to test the syntax or results either. Let me know if there are issues and we can try to figure them out.

MATCH(:Customer{Name:"Angy"})-[:PLACES_ORDER]->(o:Orders)
WITH o ORDER BY o.OrderTimestamp DESC LIMIT 5
WITH [o.FoodName] as foods, point({latitude: $myLatitude, longitude: $myLongitude}) as myPoint
MATCH (r:Restaurant)-[:SERVES]->(n:Orders)
WHERE (n.FoodName in foods)
WITH r.RestaurantName as restaurant, point.distance(point({latitude: r.RestLat, longitude: r.RestLong}), myPoint) as distance
ORDER BY distance DESC
LIMIT 3
RETURN restaurant, distance

Hi Thank you for your query: I could find the distance between my customer and a restaurant by this

MATCH(c:Customer)-[:PLACES_ORDER]->(o:Orders)-[:BELONGS_TO]->(r:Restaurant)

MATCH(o:Orders)-[:BELONGS_TO]->(r:Restaurant)

WITH point({longitude: c.Longitude, latitude: c.Latitude}) AS trainPoint, point({longitude: r.RestLong, latitude: r.RestLat}) AS officePoint

RETURN round(point.distance(trainPoint, officePoint)) AS travelDistance

ORDER BY travelDistance DESC

How do I integrate this in my final search query for recommendaitons?

MATCH(c:Customer{Name:"Ania"})-[:PLACES_ORDER]->(o:Orders)
WITH o ORDER BY o.OrderTimestamp DESC LIMIT 5
WITH [o.FoodName] as foods
MATCH (r:Restaurant)<-[:BELONGS_TO]-(o:Orders{FoodName:"Pommes"})
WHERE (o.FoodName in foods) AND NOT (:Customer{Name:"Angy"})-[:PLACES_ORDER]->(o:Orders)-[:BELONGS_TO]->(r:Restaurant)
RETURN distinct r.RestaurantName,foods

Does your final query give you the results you want? The second match basically restricts your search to only the food "Pommes" and only if "Ania" ordered it within her last 5 orders, and "Angy" could never of ordered "Pommes" as well. If that is true, you will get the Restaurants that serve "Pommes" and a list of the last 5 foods "Ania" ordered.

yes sorry thats a typo. Its ANIA on both places. and yes this is working. Now i want to integrate my distance query into this.

Why are you restricting the food to "Pommes" in the second match?

I also provided a solution using the distance in a few messages back.

yes yes , i was just trying to customise .. suppose a user wants to only know where he can find "pommes"... do you know how to integrate that distance query to my main query? and find restaurants as per the distance?

I suggest you build two separate queries, as these seem like two separate uses cases. A query to find restaurants that sell "Pommes" would be:

match (o:Orders)-[:BELONGS_TO]->(r:Restaurant)
where o.FoodName = "Pommes"
return r.RestaurantName

The issue with your data model is that someone has to order an item for the food to be introduced into your model. Typically, the food would be in its own entity, and the restaurant and orders would refer to it.

Nodes 2022
Nodes
NODES 2022, Neo4j Online Education Summit

All the sessions of the conference are now available online