Working with nodejs and neo4j. I have the node-label: Product
. one of it's properties is entries
: which is a stringfied json, that contain nested objects of type - entry
.
when ever a user enter a product link, the amount of it's entry is incremented.
e.g: Entering the product link from Facebook page, amount of facebookPage entry should be incremented.
(productId
and entry
are arguments of the server-endpoint that route to that query. )
the current query:
MATCH (p:Product {id: $prodcutId})
WITH apoc.convert.fromJsonMap(p.entries).facebookPage AS jsonEntries, p
SET p.entries = apoc.convert.toJson({facebookPage: { link: jsonEntries.link, amount: jsonEntries.amount + 1}})
RETURN p as product
with one entry (facebookPage), the query is working fine.
but with more than one(e.g: instagramPage), i need a way to save the former entries data.
with javascript i would have done something like this:
SET p.entries = apoc.convert.toJson({...jsonEntries, $entry: { link: jsonEntries.link, amount: jsonEntries.amount + 1, min: 1 }}})
Is there a way to achieve this behavior ?
i saw the APOC dot notation for destructing json object.
using it with my case, it would look something like this
MATCH (p:Product {id: 'b80a61ea4a40408f847214fa3ccf9067'})
WITH apoc.convert.fromJsonMap(l.entries) AS jsonEntries, l
SET l.entries = apoc.convert.toJson(jsonEntries{.instagramPage, facebookPage: { link: jsonEntries.facebookPage.link, amount: jsonEntries.amount + 1 }})
RETURN l as p
but this requires specifying any of the entries, which isn't desired. There will be a lot of entries, and it will make the query hard to maintain. also, the query will need to be updated any time there is a new entry.
product structure:
{
"entries": "{"facebookPage":{"amount":1,"link":"www.facebook.com"},"instagram":{"amount":1,"link":"www.IG.com"}}",
"id": "b80a61ea4a40408f847214fa3ccf9067",
"title": "Guitar"
}
}
entry structure:
{
amount: 0,
link: 'some-link.com',
}
The destructuring you're using there isn't an APOC feature but just vanilla Neo4j. You can destructure all properties using the .*
selector - see the last example on the map projection documentation page.
For you then, we'd replace .instagramPage
with .*
:
MATCH (p:Product {id: 'b80a61ea4a40408f847214fa3ccf9067'})
WITH apoc.convert.fromJsonMap(l.entries) AS jsonEntries, l
SET l.entries = apoc.convert.toJson(jsonEntries{.*, facebookPage: { link: jsonEntries.facebookPage.link, amount: jsonEntries.amount + 1 }})
RETURN l as p
Here's a minimal example showing .*
working just to play around with:
WITH {instagramPage: {link: "instagram.com"}} AS entry
RETURN entry {.*, facebookPage: {link: "facebook.com"}}
Output:
{
"facebookPage": {
"link": "facebook.com"
},
"instagramPage": {
"link": "instagram.com"
}
}
Happily, destructuring this way also replaces existing fields in the map with updated values when there's a collision:
WITH {instagramPage: {link: "instagram.com"}} AS entry
RETURN entry {.*, instagramPage: {link: "newinstagram.com"}}
Output:
{
"instagramPage": {
"link": "newinstagram.com"
}
}