ArangoDB AQL Query - merge children into parent - arangodb

I have a simple parent-child relationship in ArangoDB. Each parent document can have zero to many children. Let's say the parent document has attributes pa1, and the child docs have one attribute ca1, and a reference back to the parent _id of "_id_parent". How do I write an AQL query to return a result set like:
[{
"_key": "111",
"_id": "parent/111",
"pa1": "aaa",
"children": [{
"_key": "21",
"_id": "child/21",
"_id_parent": "parent/111",
"ca1": "www"
},
{
"_key": "22",
"_id": "child/22",
"_id_parent": "parent/111",
"ca1": "xxx"
}
]
},
{
"_key": "222",
"_id": "parent/222",
"pa1": "ddd",
"children": [{
"_key": "31",
"_id": "child/31",
"_id_parent": "parent/222",
"ca1": "yyy"
},
{
"_key": "32",
"_id": "child/32",
"_id_parent": "parent/222",
"ca1": "zzz"
}
]
}
]
In other words, how do I "flatten" this:
FOR p IN Parent
FILTER p.pa1 == #parm1
LET children = (
(FOR c IN Child
FILTER c._id_parent == p._id
RETURN c)
)
RETURN {p, children}

All what's left to do is to actually merge the children with the parent document:
RETURN MERGE(p, {children})
That is the short form of RETURN MERGE(p, {children: children}).
p is this:
{
"_id": "Parent/111",
"_key": "111",
"_rev": "_WLsUlK2--_",
"pa1": "aaa"
}
{children} or the more verbose {children: children} creates an object with a single attribute, with the name children and the value of the children variable:
{
"children": [
{
"_key": "21",
"_id": "Child/21",
"_rev": "_WLsW4Su--_",
"_id_parent": "Parent/111",
"ca1": "www"
},
{
"_key": "22",
"_id": "Child/22",
"_rev": "_WLsW8Zu--_",
"_id_parent": "Parent/111",
"ca1": "xxx"
}
]
}
MERGE() combines both objects. Since there is no overlap in attribute keys ("_id", "_key", "_rev", "pa1" vs. "children"), no top level attributes of p are replaced by children.
BTW: You can spare one pair of parentheses around the subquery expression
LET var = ( ( <subquery> ) ) - it is sufficient to do LET var = ( <subquery> ).

Related

How to perform a bidirectional upsert in ArangoDB

I'm working with a dataset similar to ArangoDB official "friendship" example, except I'm adding a "weight" concept on the Edge Collection. Like so :
People
[
{ "_id": "people/100", "_key": "100", "name": "John" },
{ "_id": "people/101", "_key": "101", "name": "Fred" },
{ "_id": "people/102", "_key": "102", "name": "Jacob" },
{ "_id": "people/103", "_key": "103", "name": "Ethan" }
]
Friendship
[
{ "_from": "people/100", "_to": "people/101", "weight": 27 },
{ "_from": "people/103", "_to": "people/102", "weight": 31 },
{ "_from": "people/102", "_to": "people/100", "weight": 12 },
{ "_from": "people/101", "_to": "people/103", "weight": 56 }
]
I want to write a function that, when someone interacts with someone else, UPSERTs the Friendship between the two (incrementing the weight by 1 if it existed before, or initializing with a weight of 1 if it's new).
The trouble is, when executing that function, I have now clue on which direction the friendship was initialized, thus I cannot really use an upsert. So 2 questions here :
Is there any way to make an upsert on an edge with "bidirectional" filter ?
Like so, but bidirectional
UPSERT {
// HERE, I BASICALLY WAN'T TO IGNORE THE SIDE
_from: ${people1}, _to: ${people2}
}
INSERT {
_from: ${people1}, _to: ${people2}, weight: 1
}
UPDATE {
weight: OLD.weight + 1
}
IN ${friendshipCollection}
RETURN NEW
Instead of trying to "select the friendship, no matter the direction"; should I rather actually duplicate the friendship on both directions (and constantly maintain / update it) ?

ArangoDb get edges with properties

I am using ArangoDb newest version and I have problem.
I have two collections:
Country (and this is document collection) and Distance (this is edge collection with keys like: _from, _to, distance).
How can I get via AQL all information about countries where Country.Continent = 'Europe' with distances between them from edge collection?
SQL would be like this:
Select * from Country c, Distance d where c.Continent = 'Europe'
Thank You.
I have been working on a project recently and started using ArangoDB so hopefully I can be of assistance to you.
I took some inspiration for my answer from the below links of the Arango and AQL documentation:
AQL Graph Traversal
Shortest Path in AQL
Please see below my AQL query and do let me know if that helped at all. You can substitute the 'Europe' part on the FILTER for #Continent which will allow you to specify it dynamically, if need be.
FOR country IN Country
FILTER country.Continent == 'Europe'
FOR vertex, edge, path
IN OUTBOUND country Distance
RETURN path
This yields the following result for me. I just created some test collections with 2 edges linking countries together. I have included the vertex, edge as well as the path of the query in the 'FOR' part, so you are welcome to play around with the 'RETURN' part at the end by substituting the vertex or edge and seeing what results that yields for you.
[
{
"edges": [
{
"_key": "67168",
"_id": "Distance/67168",
"_from": "Country/67057",
"_to": "Country/67094",
"_rev": "_aecXk7---_",
"Distance": 5
}
],
"vertices": [
{
"_key": "67057",
"_id": "Country/67057",
"_rev": "_aecWJ0q--_",
"countryName": "UK",
"Continent": "Europe"
},
{
"_key": "67094",
"_id": "Country/67094",
"_rev": "_aecWZhi--_",
"countryName": "Italy",
"Continent": "Europe"
}
]
},
{
"edges": [
{
"_key": "67222",
"_id": "Distance/67222",
"_from": "Country/67057",
"_to": "Country/67113",
"_rev": "_aecYB9---_",
"Distance": 10
}
],
"vertices": [
{
"_key": "67057",
"_id": "Country/67057",
"_rev": "_aecWJ0q--_",
"countryName": "UK",
"Continent": "Europe"
},
{
"_key": "67113",
"_id": "Country/67113",
"_rev": "_aecWmEy--_",
"countryName": "Spain",
"Continent": "Europe"
}
]
}
]
For example if you substitute the 'RETURN path' part with 'RETURN edge', you will just retrieve the edges if that is all you need, as per below:
[
{
"_key": "67168",
"_id": "Distance/67168",
"_from": "Country/67057",
"_to": "Country/67094",
"_rev": "_aecXk7---_",
"Distance": 5
},
{
"_key": "67222",
"_id": "Distance/67222",
"_from": "Country/67057",
"_to": "Country/67113",
"_rev": "_aecYB9---_",
"Distance": 10
}
]

Create mind map of mongodb objects

I want to make something like mind map from model Branches, which have "parent" and "children" value. It looks like :
Branches = [{
children: {},
_id: String,
parent: { type: Schema.Types.ObjectId, ref: 'Branch' }
}]
And i want to get something like :
[
{
"_id": "599c1f79f339dc3991d8250a",
"name": "Robert",
"children": [
{
"_id": "599c1f82f339dc3991d8250b",
"name": "Robert 1",
"parent": "599c1f79f339dc3991d8250a",
"children": [
{
"_id": "599c1f8ff339dc3991d8250c",
"name": "Robert 2",
"parent": "599c1f82f339dc3991d8250b",
"children": [
{
"_id": "599c2b7373a7d43e5205af1f",
"name": "Robert 4",
"parent": "599c1f8ff339dc3991d8250c",
"__v": 0,
"shareholder": [],
"createdDate": "2017-08-22T13:02:43.290Z",
"children": []
}
],
"__v": 0,
"shareholder": [],
"createdDate": "2017-08-22T12:11:59.230Z"
},
{
"_id": "599c1f9df339dc3991d8250d",
"name": "Robert 3",
"parent": "599c1f82f339dc3991d8250b",
"children": [],
"__v": 0,
"shareholder": [],
"createdDate": "2017-08-22T12:12:13.156Z"
}
],
"__v": 0,
"shareholder": [],
"createdDate": "2017-08-22T12:11:46.938Z"
}
],
"__v": 0,
"parent": null,
"shareholder": [],
"createdDate": "2017-08-22T12:11:37.005Z"
}
]"
If one object have value parent " 1 " , branch with _id : "1" must have him inside children object. My code now is :
Branch.find((err, foundedBranches) => {
function createTree(branch) {
function inner(parent) {
return branch.filter(x => x.parent === parent)
.map(x => {
x.children = inner(x._id)
return x
})
}
return inner(null)
}
let formated = createTree(branches)
res.json(formated)
If You know how can I resolve it, please help :/
What about something like this - you will get childs for every entry
// All items from database
const dataFromDatabase = // ...
// For each item, go and look for childrens
const finalMap = dataFromDatabase.map((x) => {
children: this.getChildren(dataFromDatabase, x._id),
_id: x._id,
// ...
});
// Get all children and for theses children go for children aswell (recursive)...
getChildren(all, id) {
return all.reduce((tmp, x) => {
if (x.parent === id) {
return [
...tmp,
{
children: this.getChildren(all, x._id),
_id: x._id,
// ...
},
];
}
return tmp;
}, []);
}
If you want to entries to only appear once, at start filter the entries to get only heads (entries that have no, parent)
dataFromDatabase.filter(x => !x.parent).map((x) => {

How to find duplicate values in mongodb using distinct query?

I am working on Mongodb distinct query, i have one collection with repeated entry, i am doing as per the created_at. But i want to fetch without repeated values.
Sample JSON
{
"posts": [{
"id": "580a2eb915a0161010c2a562",
"name": "\"Ah Me Joy\" Porter",
"created_at": "15-10-2016"
}, {
"id": "580a2eb915a0161010c2a562",
"name": "\"Ah Me Joy\" Porter",
"created_at": "25-10-2016"
}, {
"id": "580a2eb915a0161010c2a562",
"name": "\"Ah Me Joy\" Porter",
"created_at": "01-10-2016"
}, {
"id": "580a2eb915a0161010c2bf572",
"name": "Hello All",
"created_at": "05-10-2016"
}]
}
Mongodb Query
db.getCollection('posts').find({"id" : ObjectId("580a2eb915a0161010c2a562")})
So i want to know about distinct query of mongodb, please kindly go through my post and let me know.
try as follows:
db.getCollection('posts').distinct("id")
It will return all the unique IDs in the collection posts as follows:
["580a2eb915a0161010c2a562", "580a2eb915a0161010c2bf572"]
From MongoDB docs:
The example use the inventory collection that contains the following documents:
{ "_id": 1, "dept": "A", "item": { "sku": "111", "color": "red" }, "sizes": [ "S", "M" ] }
{ "_id": 2, "dept": "A", "item": { "sku": "111", "color": "blue" }, "sizes": [ "M", "L" ] }
{ "_id": 3, "dept": "B", "item": { "sku": "222", "color": "blue" }, "sizes": "S" }
{ "_id": 4, "dept": "A", "item": { "sku": "333", "color": "black" }, "sizes": [ "S" ] }
To Return Distinct Values for a Field (dept):
db.inventory.distinct( "dept" )
The method returns the following array of distinct dept values:
[ "A", "B" ]
Reference:
https://docs.mongodb.com/v3.2/reference/method/db.collection.distinct/
As per my understanding, you want to get distinct results which should eliminates the duplicate id in that collection
By using distinct in mongodb, It will return list of distinct values
db.getCollection('posts').distinct("id");
["580a2eb915a0161010c2a562", "580a2eb915a0161010c2bf572"]
So you should look into mongodb aggregation
db.posts.aggregate(
{ "$group" : { "_id" : "$id", "name" : {"$first" : "$name"}, "created_at" : {"$first" : "$created_at"} }}
)
The output will be list of results which eliminates the duplicate id documents

Cloudant/CouchDB query by content of nested array

In my cloudant database I have objects like these. I'd like to query for objects that based on properties in a nested array.
In the example below, how to I query for all objects where there is a vote with userId=="user1"? The query should return both objects. When I search for userId "user2" it should return the first one as the second object only has vote from user1 and user4.
{
"_id": "1",
"votes": [
{
"userId": "user1",
"comment": ""
},
{
"userId": "user2",
"comment": ""
},
{
"userId": "user3",
"comment": ""
}
]
}
{
"_id": "2",
"votes": [
{
"userId": "user1",
"comment": ""
},
{
"userId": "user4",
"comment": ""
}
]
}
This view will return a list of all votes (where a vote is userId and _id) sorted by userId, to only get user1 use ?key="user1"
function(doc) {
for(i in doc.votes)
emit(doc.votes[i].userId, doc._id);
}
/dbName/_design/foo/_view/bar?key="user1"
{"total_rows":5,"offset":0,"rows":[
{"id":"1","key":"user1","value":"1"},
{"id":"2","key":"user1","value":"2"}
]}

Resources