On removing stale relationships - neo4j

A shopping cart is being logged to Neo4j.
NOTE: Each cart is unique to a visitor and defined by the cart:path (which is actually a cart ID cookie). Each item is a lineitem in the cart. Its unique to the cart and product in the cart (unique key is item.key). Finally, product_id refers to a product.
The cart contains lines which need to be updated even when someone deletes a line or changes quantity.
The set updates values for existing lines, but it wouldn't remove lines that are deleted from the cart when the updated cart json arrives.
Is there a simple way to modify this query to delete removed lines automatically?
UNWIND items as item
MATCH (p:Cart {path:px.upath})
SET p.total_price=cart.total_price
MERGE (i:Item {key:item.key})
SET
i.product_id=item.product_id,
i.quantity=item.quantity,
MERGE (i)-[:LineOf]->(p)

I'm a little puzzled by the query, since it seems like you're merging in items as they're added to the cart, as opposed to matching on existing products. Also, the :Item nodes you're adding seem specific for a single user's transaction (you're setting the quantity based on the input), so it seems unwise to use MERGE here...what if two users are trying to add the same type of item at the same time, but different quantities? One of those transactions will overrule the other...user 1 adds 2 of :Item a, but user 2 adds 10 of item a, which changes user 1's :Cart to now read 10 of :Item a selected (though the :Cart's total hasn't been updated to that...) It seems like the model could use some improvements.
One way you could do this is to set the quantity on the relationship to the cart, instead of on the item.
Something like this:
UNWIND items as item
MATCH (p:Cart {path:px.upath})
SET p.total_price=cart.total_price
MERGE (i:Item {key:item.key})
// doesn't seem the place to set product_id, but leaving it in
SET i.product_id=item.product_id
MERGE (i)-[r:LineOf]->(p)
SET r.quantity=item.quantity
So it seems like this query is for adding items to the cart, and you need a query for deleting items.
This should do the trick:
// assume toDelete is your list of items to delete
WITH toDelete
MATCH (p:Cart {path:px.upath})
SET p.total_price=cart.total_price
MATCH (p)<-[r:LineOf]-(i)
WHERE i.key in toDelete
DELETE r
If you want a single query that will set your items as in your first query, and delete all other lines that are not sent over, then this combined query should work:
UNWIND items as item
MATCH (p:Cart {path:px.upath})
SET p.total_price=cart.total_price
// first delete all items from the cart that aren't listed
OPTIONAL MATCH (p)<-[r:LineOf]-(i)
WHERE NOT i.key in items
DELETE r
// now add/update the rest
MERGE (i:Item {key:item.key})
// doesn't seem the place to set product_id, but leaving it in
SET i.product_id=item.product_id
MERGE (i)-[r:LineOf]->(p)
SET r.quantity=item.quantity

Related

building recommendation system using weight as relation in neo4j

in my graph we have users that have 2 relation called "favorite" and "seen" with products and product have relation called has with some specification as colors(red blue...) , types(jeans....) and sizes (30...)
so i make some query that when i wanna create favorite or seen relation , it makes a relation with that specific user and the specification of that product calling "weight" and set property for that called "score" and i wanna increase this score every time user that set product to favorite or just seeing that product for example when a user see the product score change to +10 and for favorite change to +20 and then we recommend products with specifications that have most score
my query is
match (user:Users{m_id:""}),(m:Products{m_id:""})-[:HAS]->(a:Specifications)
MERGE (user) -[:FAVORITE]-> (m)
merge (user)-[:WEIGHT{score:0}]->(a)
and one more problem with this query is i dont wanna make new relation if i already have it i just wanna increase the scoreenter image description here
You would only merge on the relationship and then set the property accordingly.
MERGE (user)-[w:WEIGHT]-(a)
ON CREATE SET w.score = 10
ON MATCH SET w.score = w.score+10
Or alternatively update it unconditionally
MERGE (user)-[w:WEIGHT]-(a)
SET w.score = coalesce(w.score,0)+10

Neo4j Cypher query to fetch posts created by followers

I am creating an app kind of like Facebook. It is an app where people can share products and collections of products. In the "create a post" popup, people can either select a product or a collection (group of products but consider it as a single object) or just text to create a post. I need to fetch the posts created by my followers.
Each post will have a property of type PRODUCT, COLLECTION, OR TEXT to indicate what type of post it is.
In my neo4j DB, there is a Post object, product object, collection object and user object.
When you create a post, relations will be created between them.
(post)-[:CREATED_BY]->(USER)
(post{type:"PRODUCT"})-[:INCLUDES]->(product)
(post{type:"COLLECTION})-[:INCLUDES]->(collection)
This is what I tried to get the posts of type "PRODUCT". IT shows an error. but just to give a basic idea of our properties.
MATCH (user:User{lastName: "mylastname"})-[:FOLLOWS {status: "accepted"}]->(following) WITH following
OPTIONAL MATCH (post:Post {type: "PRODUCT"})-[r:CREATED_BY]->(following) WITH post,user, r OPTIONAL
MATCH
(post)-[:INCLUDES]->(product:Product) WITH COLLECT({post:post, datetime: r.datetime,
type:"PRODUCT",product:product user: following}) as productPosts
UNWIND productPosts AS row
RETURN row
ORDER BY row.datetime DESC
SKIP 0
LIMIT 10
Your WITH clauses are not specifying all the variables that need to be carried forward to the remainder of the query. Also, there has at least one typo (a missing comma).
In fact, your query does not even need any WITH clauses. Nor does it need to COLLECT a list only to immediately UNWIND it.
This query should work better:
MATCH (user:User{lastName: "mylastname"})-[:FOLLOWS {status: "accepted"}]->(following)
OPTIONAL MATCH (post:Post {type: "PRODUCT"})-[r:CREATED_BY]->(following)
OPTIONAL MATCH (post)-[:INCLUDES]->(product:Product)
RETURN {post:post, datetime: r.datetime, type:"PRODUCT", product:product, user: following} AS row
ORDER BY row.datetime DESC
LIMIT 10

How to delete a ets entry based on non key part

I have a etc table ‘table’ as {key,[val1,val2]}
I selected this data from the table using
ets:select(table,[{{‘$1','$2'},[],['$$']}]).
[[key,["val1",<<"12">>]],
[key,["val2",<<"6">>]],
[key,["val3",<<"16">>]]]
I want to delete a entry matching the part [val1,val2] using this
ets:select_delete(table,[{{‘$1','$2'},[{'==','$2',["val1",<<"12">>]}],['$$']}]).
0
But still when I run select again I get
ets:select(table,[{{‘$1','$2'},[],['$$']}]).
[[key,["val1",<<"12">>]],
[key,["val2",<<"6">>]],
[key,["val3",<<"16">>]]]
How can I delete this entry based on the non key part?
The ets:select_delete documentation says:
The match specification has to return the atom true if the object is to be deleted. No other return value gets the object deleted. So one cannot use the same match specification for looking up elements as for deleting them.
So try this:
ets:select_delete(table,[{{'$1','$2'},[{'==','$2',["val1",<<"12">>]}],true}]).
ets:select_delete returns the number of records it deleted, so hopefully it should return 1 this time.

Ecommerce (Spree) print count of products with completed orders on Index page. (postgres)

On our product gallery page, I'd really like to query the database to find how many of each product has been purchased, and print that number on the page (for admin users only). I feel like I have all the pieces, I just don't know how to put it together to work.
There is a Products table that is referenced to print out all the products.
There is an LineItems table (Spree::LineItems >> Spree::Orders) where the product can be called/identified by:
#line_items.each do |line_item|
line_item.product.id
end
& this is what defines an order as complete in relation to that line_item:
#line_item.order.state == "complete"
So...I'd like to see if #product.id and #line_item.product.id match (where the #line_item.order.state == "complete") and count how many.
Basically, we iterate each product on the gallery page, so I want to see for each product how many times it appears as a line_item in an order where the order.state is complete
I'm no engineer (as you can obviously tell), but I feel like I'm making this more complicated than it needs to be. Help?
We're using a Postgres database. Thanks in advance!!
That should give you a count of line items associated at the same time with a product with the given id and an order with 'complete' state.
LineItem.joins(:product, :order).where(orders: { state: 'complete' }).where(products: { id: your_product_id }).count
No need to iterate! Just a database query (using ActiveRecord), which also is extremely more efficient than iteration.
I assumed your tables are called orders, products (used in the where clauses) and the associations on the LineItem ActiveRecord object are called order, product (used in the join). Otherwise simply replace those values with the appropriate ones.

neo4j Multiple optional paths - cypher

I'm tracking if a user has liked and or voted on a object in a list of objects others posted.. I can get either likes and votes, but not both. (A person can both like and vote on an object and these options are not mutually exclusive).
To simply this problem let me describe it in relational terms (left joins used - object is ALWAYS returned, liker and voter data is only returned if a record of that type exists)
[object]+ -> liker
+ -> voter
What I'd like to return is:
objectID likerID voterID
2343 null 88
2345 11 null
2382 44 1256
2400 null null
Yet every which way I've sliced I cannot get it to come out like that . Either row 2400 is skipped (I've tried every combination of where), or values are even shifted from likerID to the voterID column (bug?).
Here is a sample of the cypher:
start objects=node(158)
match contestant-[:POSTED]->object_node-[:POSTED_OBJECT]->objects<-[?:POSTED_OBJECT]-object_node_a<-[?:LIKES]-liker
, objects<-[?:POSTED_OBJECT]-object_node_b<-[?:VOTES]-voter
return id(object, id(liker), id(voter)
It doesn't work even if I try where id(object_node_a) = id(object_node_b)...
If I just try to get a liker it works.. same with voter.. but when I try to do both.. bombs..
I've tried using where , etc but ultimately I never get the full list of objects - it either trims down the list based upon matches, or gives me the Cartesian product which distinct does not resolve.
SQL EXAMPLE: LEFT JOIN
I'm a sql guy so let me explain it this way - I have a objects table on the left, and I want to left join it to a liker table and a voter table, and return both the liker id and voter id on a single row along with the object data. All the object records will be returned regardless if there is a voter or liker record.
[object]+ -> liker
+ -> voter
IS THIS EVEN POSSIBLE?
Is it possible to do this via cypher?
Hopefully I haven't misunderstood. To get
objectID likerID voterID
2343 null 88
2345 11 null
2382 44 1256
2400 null null
i.e. all objects and the ID of those that liked it and voted for it, this query should do it-
start o=<lookup for objects>
match ul-[like?:LIKED]->o, uv-[vote?:VOTED]->o
return o,ID(ul),ID(uv)
This will return objects that no votes and likes, both votes and likes and either one. Note that if you have multiple users voting for the same object as is likely, then your object row will repeat for each user. You might want to do something like
start o=<lookup for objects>
match ul-[like?:LIKED]->o, uv-[vote?:VOTED]->o
return o,collect(ID(ul)),collect(ID(uv))
to still get a row per object but a collection of user IDS for votes and likes.
To include the person that posted the object as well:
start o=node(4,5,6,7)
match ul-[like?:LIKED]->o, uv-[vote?:VOTED]->o, c-[:POSTED_OBJECT]->o
return o,ID(ul),ID(uv),ID(c)
I created a tiny sample to play with: http://console.neo4j.org/r/in8g4w

Resources