How to normalize this recursive nested JSON - normalization

Having a bit of an issue trying to normalise the my payload that contains a nested schema of the same type as the parent using Normalizr
For example
{
id: 123,
sections:{
section: [{
id: 1,
name: "test",
sections: {
section: {
id: 125,
name: "test125"
}
}
}, {
id: 2,
name: "test2"
sections: {
section: [
{
id: 124,
name: "test124"
}
]
}
}, {
id: 3,
name: "test3"
}]
}
}
In the above json structure, nested section may be an object or an array.

I had a similar issue with nested comments. Here's how I solved it:
export const comment = new schema.Entity("comment", {}, {
idAttribute: "key"
})
comment.define({
user: user,
reactions: {
data: [reaction]
},
children: [comment]
})
export const post = new schema.Entity("post", {
user: user,
files: [image],
comments: {
data: [comment]
},
reactions: {
data: [reaction]
}
}, {
idAttribute: "key"
})
Basically, I define comment as a new entity before I use it in its own entity definition. The other schemas I define as usual, in the constructor (see the post schema for an example). Hope this helps.

Here is a jq filter which will normalize the data:
def enumerate:
if type=="array" then .[] else . end ;
def sectionids:
[ .sections.section | enumerate | .id // empty | tostring ]
| if .==[] then {} else {sections:.} end ;
def sections:
{sections:{section:[.]}}
| .. | .section? | enumerate | objects | del(.sections) + sectionids ;
{
"result": .id,
"entities": {
"sections": (reduce sections as $s ({};.["\($s.id)"]=$s))
}
}
Sample Run (assumes proper json data in data.json and the above filter in filter.jq)
$ jq -M -f filter.jq data.json
{
"result": 123,
"entities": {
"sections": {
"123": {
"id": 123,
"sections": [
"1",
"2",
"3"
]
},
"1": {
"id": 1,
"name": "test",
"sections": [
"125"
]
},
"2": {
"id": 2,
"name": "test2",
"sections": [
"124"
]
},
"3": {
"id": 3,
"name": "test3"
},
"125": {
"id": 125,
"name": "test125"
},
"124": {
"id": 124,
"name": "test124"
}
}
}
}
Try it online!

Related

Filter empty object in odata

There is an oData service i want to call. We have an model that has an field Parent. The service tells me that the parent is an object with an iv that is an string inside. So we get the following model from the oData service:
{
"total": 0,
"items": [
{
"id": "string",
"data": {
"Title": {
"en": "string",
"nl": "string"
},
"Parent": {
"iv": [
"string"
]
},
}
]
}
Now when we get data back it looks like this:
{
"total": 2,
"items": [
{
"id": "6204c07d-1aef-4bd2-9646-e3ca36c63784",
"data": {
"Title": {
"en": "Test 1",
"nl": "Test 1"
},
"Parent": {
"iv": []
},
},
},
{
"id": "bfd1b084-4166-4fec-9032-08047c8313d2",
"data": {
"Title": {
"en": "Test 2",
"nl": "Test 2"
},
"Parent": {
"iv": [
"6204c07d-1aef-4bd2-9646-e3ca36c63784"
]
},
},
},
}
Now i want to filter on all the items where parent is [] (so no parent selected). I've tried the following filters but all without success
data/Parent/iv eq [] | Returns 0 results
data/Parent/iv eq null | Returns 0 results
data/Parent eq null | Returns 0 results
length(data/Parent/iv) eq 0 | OData operation is not supported
not data/Parent/any() | Any/All may only be used following a collection.
not data/Parent/any() | Any/All may only be used following a collection.
data/Parent/iv/$count eq 0 | Server error
Found that for this case the following works:
empty(data/Parent/iv)

Modifying cypher query

I have neo4j data as like as given below:
Here I have COUNTEY_PROVINCE relationship between country and province, PROVINCE_CITY relationship between province and city and COUNTRY_CITY relationship between country and city. When user call an api with country name, I want to return all province with city. To do this, I have run the following query:
MATCH path=(cn:Country { name: "Bangladesh" })-[:COUNTRY_PROVINCE]->(pv:Province)-[:PROVINCE_CITY]->(ct:City)
RETURN { x: nodes(path) }
And I have got the following result ( country, province, city ) :
{ x: nodes(path) }
{ "x": [ { "name": "Bangladesh"}, { "name": "Dhaka" }, { "name": "Dhaka" } ] }
{ "x": [ { "name": "Bangladesh"}, { "name": "Dhaka" }, { "name": "Narayanganj" } ] }
{ "x": [ { "name": "Bangladesh"}, { "name": "Sylhet" }, { "name": "Sylhet" } ] }
{ "x": [ { "name": "Bangladesh"}, { "name": "Khulna" }, { "name": "Khulna" } ] }
{ "x": [ { "name": "Bangladesh"}, { "name": "Khulna" }, { "name": "Jessore" } ] }
{ "x": [ { "name": "Bangladesh"}, { "name": "Chittagong" }, { "name": "Chittagong" } ] }
{ "x": [ { "name": "Bangladesh"}, { "name": "Chittagong" }, { "name": "Comilla" } ] }
Now my question is, how can I get country list with province and associate cities like :
[
{
country: {
name: "Bangladesh",
province: [
{
name: "Dhaka",
city: [
{ name: "Dhaka" },
{ name: "Narayanganj" }
]
},
{
name: "Sylhet",
city: [
{ name: "Sylhet" }
]
},
{
name: "Chittagong",
city: [
{ name: "Chittagong" },
{ name: "Comilla" }
]
},
{
name: "Khulna",
city: [
{ name: "Khulna" },
{ name: "Jessore" }
]
}
]
}
}
]
I simulated your scenario here.
Load the initial data set (Similar to the data set described in the question):
CREATE (c:Country {name:"Country A"})
CREATE (p1:Province {name:"Province A"})
CREATE (p2:Province {name:"Province B"})
CREATE (c1:City {name:"City A"})
CREATE (c2:City {name:"City B"})
CREATE (c3:City {name:"City C"})
CREATE (c4:City {name:"City D"})
CREATE (c)-[:COUNTRY_PROVINCE]->(p1)
CREATE (p1)-[:PROVINCE_CITY]->(c1)
CREATE (p1)-[:PROVINCE_CITY]->(c2)
CREATE (c)-[:COUNTRY_PROVINCE]->(p2)
CREATE (p2)-[:PROVINCE_CITY]->(c3)
CREATE (p2)-[:PROVINCE_CITY]->(c4)
The query:
MATCH (cn:Country { name: "Country A" })-[:COUNTRY_PROVINCE]->(pv:Province)-[:PROVINCE_CITY]->(ct:City)
WITH cn, pv, collect({name : ct.name}) as cities
RETURN {coutry : {name : cn.name, province : collect( distinct { name:pv.name, city :cities }) } }
The result:
{
"coutry":{
"name":"Country A",
"province":[
{
"name":"Province A",
"city":[
{
"name":"City B"
},
{
"name":"City A"
}
]
},
{
"name":"Province B",
"city":[
{
"name":"City D"
},
{
"name":"City C"
}
]
}
]
}
}
The above query uses the collect() function and the DISTINCT operator to achieve the desired format.
Well, if you're going to return the nodes of a path, that's obviously what you are going to get. You've got several options to do it differently :
Check out https://neo4j-contrib.github.io/neo4j-apoc-procedures/index32.html#_from_tojson, for example apoc.convert.toTree may get you very close to what you want.
Do it yourself. Send the query from any client application and process (+ format) the results yourself (in Java, Python, C#, ...)
...
Hope this helps.
Regards,
Tom

Relationship not being created, match by Id not returning results

I'm just getting started with Neo4j.
I'm trying to do a complex LOAD FROM CSV but I don't seem to be getting the relations part right.
LOAD CSV WITH HEADERS FROM "file:/Users/brestrepo/Krossover/neo4j/access_control_games.csv" AS csvLine
MERGE (game:Game
{
id: toInt(csvLine.id),
datePlayed: csvLine.date_played,
type: csvLine.type,
createdAt: csvLine.created_at,
updatedAt: csvLine.updated_at
}
)
FOREACH(ignoreMe IN CASE WHEN trim(csvLine.deleted_at) <> "" THEN [1] ELSE [] END | SET game.deletedAt = csvLine.deleted_at)
WITH csvLine, game
MATCH (team:TEAM { id: toInt(csvLine.created_by_team_id)}),(game:Game { id: toInt(csvLine.id)})
CREATE (team)-[:OWNER_TEAM { }]->(game)
WITH csvLine, game
MATCH (user:User { id: toInt(csvLine.created_by_user_id)}),(game:Game { id: toInt(csvLine.id)})
CREATE (user)-[:OWNER_USER { }]->(game)
WITH csvLine, game
MATCH (team_away:TEAM { id: toInt(csvLine.team_away_id)}),(game:Game { id: toInt(csvLine.id)})
CREATE (team_away)-[:AWAY_TEAM { }]->(game)
WITH csvLine, game
MATCH (team_home:TEAM { id: toInt(csvLine.team_home_id)}),(game:Game { id: toInt(csvLine.id)})
CREATE (team_home)-[:HOME_TEAM { }]->(game)
RETURN game
I think that the MATCH is that's causing this problem. The Match is unable to find a game.
I do get games when I do:
MATCH (game:Game) return game LIMIT 1
{
"records": [
{
"keys": [
"game"
],
"length": 1,
"_fields": [
{
"identity": {
"low": 31891,
"high": 0
},
"labels": [
"Game"
],
"properties": {
"createdAt": "2014-07-18 19:38:32",
"deletedAt": "NULL",
"id": {
"low": 285,
"high": 0
},
"type": "2",
"datePlayed": "2013-10-12 04:00:00",
"updatedAt": "2016-03-25 15:28:41"
},
"id": "31891"
}
],
"_fieldLookup": {
"game": 0
}
}
],
}
But then, when I do
MATCH (game:Game {id: 31891}) return game;
(no changes, no records)
Can anyone point me in the right direction?

Cypher results return certain structure

I'm trying to return a certain structure.
Here is my query:
MATCH (tracker:tracker { active: true }) OPTIONAL MATCH (tracker { active: true })--(timer:timer) RETURN { tracker:tracker, timers:COLLECT(timer) } as trackers
Here is what I am returning so far:
{
"results": [{
"columns": ["trackers"],
"data": [{
"row": [{
"tracker": {
"title": "a",
"id": "04e3fddc-5aef-4c3a-9aeb-62a9fb15bd75",
"active": true
},
"timers": []
}]
}]
}],
"errors": []
}
I would like the timers to be nested under the "tracker" with the tracker's properties, like this:
{
"results": [{
"columns": ["trackers"],
"data": [{
"row": [{
"tracker": {
"title": "a",
"id": "04e3fddc-5aef-4c3a-9aeb-62a9fb15bd75",
"active": true,
"timers": []
}]
}]
}],
"errors": []
}
Try this:
MATCH (tr:tracker {active: true})
OPTIONAL MATCH (tr)--(ti:timer)
WITH {
title: tr.title,
id: tr.id,
active: tr.active,
timers: COLLECT(ti)
} as trackers
RETURN trackers

Merge an Array of Documents in RethinkDB

Let's say I have a table called bookshelf. I create a bookshelf document that has an array of IDs that references books in another table called books. I am trying to use getAll and merge to combine the documents in a query but I cannot seem to be able to pass all the elements into getAll. I believe getAll is interpreting the array of IDs as a single literal "object".
A bookshelf document:
{
id: 'MyShelf'
books: [ 1, 2, 6, 9 ]
}
A book document:
{
id: 1
name: 'Merging in RethinkDB for Dummies'
}
My query:
r.db('test').table('bookshelf').merge(function(bookshelf) {
return {
booksOnShelf: r.table('books').getAll(bookshelf('books')).coerceTo('array')
}
})
Expected result:
{
id: 'MyShelf'
books: [ 1, 2, 6, 9 ],
booksOnShelf: [
{ id: 1, name: 'Merging in RethinkDB for Dummies' },
{ id: 2, name: 'Cool Title!' },
...
]
}
Actual result:
{
id: 'MyShelf'
books: [ 1, 2, 6, 9 ],
booksOnShelf: [ ]
}
It turns out that I needed to use r.args to turn the array into actual arguments for use with the getAll function.
Here is the correct query:
r.db('test').table('bookshelf').merge(function(bookshelf) {
return {
booksOnShelf: r.table('books').getAll(r.args(bookshelf('books'))).coerceTo('array')
}
})
And the result:
{
"books": [ 1, 2 ],
"booksOnShelf": [{
"id": 1,
"title": "RethinkDB for Dummies"
}, {
"id": 2,
"title": "Awesome Title!"
}],
"id": "MyShelf"
}

Resources