I am looking for how to use $graphLookup and its match condition to return all nodes until a condition is met, including matches breaks the condition in the recursion stack. the first node that satisfies the condition in the recursion stack on each separate branch.
This should work for an arbitrary topology.
EDIT: as Asya highlights, it's the first time that the condition is satisfied on each branch, and not on the entire recursion stack.
Example (note: "_id" fields in docs are omitted for brevity):
Data:
[
{
"key": 1,
"parent": null,
"name": "tom"
},
{
"key": 2,
"parent": 1,
"name": "tom"
},
{
"key": 3,
"parent": 2,
"name": "jack"
},
{
"key": 4,
"parent": 3,
"name": "jonny"
},
{
"key": 5,
"parent": 1,
"name": "jack"
},
{
"key": 6,
"parent": 5,
"name": "jack"
}
]
Query:
db.collection.aggregate([
{
"$match": {
"parent": null
}
},
{
"$graphLookup": {
"from": "collection",
"startWith": "$key",
"connectFromField": "key",
"connectToField": "parent",
"as": "children",
"restrictSearchWithMatch": {
"name": "jack"
},
"matchType":"firstHitAlongEachBranch" // made-up option
}
}
])
Desired result:
[
{
"children": [
{
"key": 2,
"name": "tom",
"parent": 1
},
{
"key": 3,
"parent": 2,
"name": "jack"
},
{
"key": 5,
"parent": 1,
"name": "jack"
},
],
"key": 1,
"name": "tom",
"parent": null
}
]
The result returns only the first of the three nodes (as expected).
Thank you
Apparently it's not possible to date.
There's an open Jira for it.
Related
The bounty expires in 3 days. Answers to this question are eligible for a +50 reputation bounty.
Ranjith V wants to draw more attention to this question.
I have the following object saved in a jsonb column in Postgres db.
{
"method": "method",
"drive": [
{
"name": "C_Drive",
"key": [
{
"is_active": true,
"created_at": "2023-01-01T00:00:00",
"id": 3,
"value": "value3"
},
{
"is_active": false,
"created_at": "2022-12-01T00:00:00",
"id": 2,
"value": "value2"
},
{
"is_active": false,
"created_at": "2022-11-01T00:00:00",
"id": 1,
"value": "value1"
}]
},
{
"name": "D_Drive",
"key": [
{
"is_active": true,
"created_at": "2023-01-01T00:00:00",
"id": 4,
"value": "value4"
}
}]
}
I want to delete a particular key inside the drive. Say I want to delete key with "id" = 3. How do i do it?
I have a document with a tree structure:
{
"_id": "62e1f19f094a5696fd18f4e9",
"parent": null,
"children": [
{
"_id": "44e1f19f094a5696fd18f4o7",
"parent": "62e1f19f094a5696fd18f4e9",
"children": [
{
"_id": "62e1f19f094a5696fd18f4e9",
"parent": "44e1f19f094a5696fd18f4o7",
"children": []
}
]
}
]
}
I want to add a new field "id" (without the underscore) which refers to "_id" to each document even if it is a child or parent
I tried to do something like this:
$addFields: {id: $_id, children.id: $children._id} but in doesn't work
so, the final result I want to get
{
"_id": "62e1f19f094a5696fd18f4e9",
"id": "62e1f19f094a5696fd18f4e9",
"parent": null,
"children": [
{
"_id": "44e1f19f094a5696fd18f4o7",
"id": "44e1f19f094a5696fd18f4o7",
"parent": "62e1f19f094a5696fd18f4e9",
"children": [
{
"_id": "62e1f19f094a5696fd18f4e9",
"id": "62e1f19f094a5696fd18f4e9",
"parent": "44e1f19f094a5696fd18f4o7",
"children": []
}
]
}
]
}
Based on a question by #rickhg12hs:
It can be done, but I agree with #turivishal that if you are doing it, it is reasonable to store it once (a minor change to the query):
db.collection.aggregate([
{
$replaceRoot: {
newRoot: {
$function: {
body: "function drill(r) {r.id = r._id; if (r.children.length > 0) { for (let elem of r.children) { drill(elem)}} return r};",
args: [
"$$ROOT"
],
lang: "js"
}
}
}
}
])
See how it works on the playground example
For storing on the db, instead of aggregate use an update with pipeline, like this
I have a customer microservice (customer document) and a bank account microservice (bank account document). How do I structure these requirements?
There are two types of clients: personal and business.
A personal client can only have a maximum of one savings account, one checking account or fixed term accounts.
A business client may not have one savings or fixed-term account but may have multiple checking accounts.
In the customers document I have an ID attribute that identifies the bank account and that could fulfill the first requirement, however, the second one indicates that you can have multiple checking accounts if it is a business type.
----------------------
EDIT:
More requirements were added.
Business bank accounts may have one or more account holders and zero or more authorized signatories.
The system must allow to consult all the movements of a bank account that a client has and the type (withdrawal or deposit).
db
db={
"clients": [
{
"_id": 1,
"type": "personal",
"name": "Tom",
"createAt": ISODate("2022-01-10T11:23:25.184Z")
},
{
"_id": 2,
"type": "business",
"name": "Apple",
"createAt": ISODate("2022-01-12T05:10:42.220Z")
}
],
"accounts": [
{
"_id": 1,
"client_id": 1,
"type": "saving",
"money": 12000
},
{
"_id": 2,
"client_id": 1,
"type": "checking",
"money": 8000
},
{
"_id": 3,
"client_id": 2,
"type": "checking",
"money": 6000
},
{
"_id": 4,
"client_id": 2,
"type": "checking",
"money": 7000
}
]
}
aggregate
db.clients.aggregate([
{
"$lookup": {
"from": "accounts",
"localField": "_id",
"foreignField": "client_id",
"as": "account_docs"
}
}
])
mongoplayground
db
db={
"clients": [
{
"_id": 1,
"type": "personal",
"name": "Tom",
"createAt": ISODate("2022-01-10T11:23:25.184Z")
},
{
"_id": 2,
"type": "business",
"name": "Apple",
"createAt": ISODate("2022-01-12T05:10:42.220Z")
},
{
"_id": 3,
"type": "business",
"name": "Apple2",
"createAt": ISODate("2022-01-13T05:10:42.220Z")
}
],
"accounts": [
{
"_id": 1,
"type": "saving",
"money": 12000
},
{
"_id": 2,
"type": "checking",
"money": 8000
},
{
"_id": 3,
"type": "checking",
"money": 6000
},
{
"_id": 4,
"type": "checking",
"money": 7000
}
],
"clientRoles": [
{
"_id": 1,
"client_id": 1,
"account_id": 1,
"type": "holder"
},
{
"_id": 2,
"client_id": 2,
"account_id": 3,
"type": "holder"
},
{
"_id": 3,
"client_id": 3,
"account_id": 3,
"type": "signatory"
}
],
"clientMovements": [
{
"_id": 1,
"client_id": 1,
"account_id": 1,
"type": "deposit",
"money": 20000
},
{
"_id": 2,
"client_id": 1,
"account_id": 1,
"type": "withdraw",
"money": 8000
}
]
}
aggregate
db.clients.aggregate([
{
"$match": {
"_id": 1
}
},
{
"$lookup": {
"from": "clientMovements",
"localField": "_id",
"foreignField": "client_id",
"as": "movement_docs"
}
}
])
mongoplayground
I'm trying to create an indented tree e.g. as in https://observablehq.com/#d3/indented-tree
I think that what this example does which I can't replicate in vega is encapsulated in this code:
root = { let i = 0; return d3.hierarchy(data).eachBefore(d => d.index = i++); }
eachBefore is a pre-order traversal on the output of d3.hierarchy.
Is there any way to get this result from (upstream) vega, or is this a feature request for an index output from the tree transform? (/something similar, or else a custom transform)
By the way, I think it may be easy to turn the specific tree layout example into an indented tree because the id happens to give the same 'index' ordering (I think), but think we need to use eachBefore where the data isn't so conveniently ordered.
Thanks for any suggestions!
Declan
Update
I made a change in vega-hierarchy described here:
https://github.com/declann/vega/commit/a651ff36cd3f0897054aa1b236f82e701db62432
Now I can use pre_traversal_id from tree transform output to do indented trees, e.g.:
indented tree in (custom) vega-editor, with tree output including pre_traversal_id field
Modified spec: https://gist.github.com/declann/91fd150ae04016e5890a30295fa58a07
Not sure if this help, but, when I enter at vega.github.io/vega/examples/tree-layout I played with the controls and (after change the settings to: layout:tidy - links:orthogonal - separation:true) I got a similar result you shown in the observablehq page:
Open the Chart in the Vega Editor
Code:
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "An example of Cartesian layouts for a node-link diagram of hierarchical data.",
"width": 600,
"height": 1600,
"padding": 5,
"signals": [
{
"name": "labels", "value": true
},
{
"name": "layout", "value": "tidy"
},
{
"name": "links", "value": "orthogonal"
}
],
"data": [
{
"name": "tree",
"url": "data/flare.json",
"transform": [
{
"type": "stratify",
"key": "id",
"parentKey": "parent"
},
{
"type": "tree",
"method": {"signal": "layout", "value": "tidy"},
"size": [{"signal": "height"}, {"signal": "width - 100"}],
"separation": true,
"as": ["y", "x", "depth", "children"]
}
]
},
{
"name": "links",
"source": "tree",
"transform": [
{ "type": "treelinks" },
{
"type": "linkpath",
"orient": "horizontal",
"shape": {"signal": "links"}
}
]
}
],
"scales": [
{
"name": "color",
"type": "linear",
"range": {"scheme": "greys"},
"domain": {"data": "tree", "field": "depth"},
"zero": true
}
],
"marks": [
{
"type": "path",
"from": {"data": "links"},
"encode": {
"update": {
"path": {"field": "path"},
"stroke": {"value": "#828282"}
}
}
},
{
"type": "symbol",
"from": {"data": "tree"},
"encode": {
"enter": {
"size": {"value": 25}
},
"update": {
"x": {"field": "x"},
"y": {"field": "y"},
"fill": {"field": "depth"}
}
}
},
{
"type": "text",
"from": {"data": "tree"},
"encode": {
"enter": {
"text": {"field": "name"},
"baseline": {"value": "middle"}
},
"update": {
"x": {"field": "x"},
"y": {"field": "y"},
"dx": {"signal": "datum.children ? -7 : 7"},
"align": {"signal": "datum.children ? 'right' : 'left'"}
}
}
}
]
}
I believe this is possible by adding expression transforms for x (based on tree_node depth) and y (based on the tree node id)
The gist of it is to transform x and y after the tree transform
{"type": "stratify", "key": "id", "parentKey": "parent"},
{
"type": "tree",
"method": {"signal": "layout"},
"size": [{"signal": "height"}, {"signal": "width - 100"}],
"separation": {"signal": "separation"},
"as": ["_", "_", "depth", "children"]
},
{"type": "formula", "expr": "datum.depth * 20", "as": "x"},
{"type": "formula", "expr": "datum.id * 14", "as": "y"}
Here's an example that modifies the Vega Tree layout example
I have my json object like this
{
"_id": "5c2e811154855c0012308f00",
"__pclass": "QXRzXFByb2plY3RcTW9kZWxcUHJvamVjdA==",
"id": 44328,
"name": "Test project via postman2//2",
"address": "some random address",
"area": null,
"bidDate": null,
"building": {
"name": "Health Care Facilities",
"type": "Dental Clinic"
},
"collaborators": [],
"createdBy": {
"user": {
"id": 7662036,
"name": "Someone Here"
},
"firm": {
"id": 2520967,
"type": "ATS"
}
},
"createdDate": "2019-01-03T21:39:29Z",
"customers": [],
"doneBy": null,
"file": null,
"firm": {
"id": 1,
"name": "MyFirm"
},
"leadSource": {
"name": "dontknow",
"number": "93794497"
},
"location": {
"id": null,
"city": {
"id": 567,
"name": "Bahamas"
},
"country": {
"id": 38,
"name": "Canada"
},
"province": {
"id": 7,
"name": "British Columbia"
}
},
"modifiedBy": null,
"modifiedDate": null,
"projectPhase": {
"id": 1,
"name": "pre-design"
},
"quotes": [{
"id": 19,
"opportunityValues": {
"Key1": 100,
"Key2 Key2": 100,
"Key3 Key3 Key3": 200,
}
}],
"specForecast": [],
"specIds": [],
"tags": [],
"valuation": "something"
}
I am trying to aggregate using this query in MongoDB. My aggregation key is 4 level deep and also contains spaces. On all online examples shows me the aggregation at the first level. Looking to the online codes, I tried to re-iterate the same with my 4th level deep key.
db.mydata.aggregate([
{$match: {"id": 44328 } } ,
{$group: { _id: "$quotes.id",
totalKey2:{ $sum: "$quotes.opportunityValues.Key2 Key2"},
totalKey3:{ $sum: "$quotes.opportunityValues.Key3 Key3 Key3"}
}
}
]);
This should return
_id totalKey2 totalKey3
0 19 100 300
But it is returning
_id totalKey2 totalKey3
0 19 0 0
What am I doing Wrong?
Although it's not recommended to use space in field names in Mongo, it works as expected.
The problem with your query is that "quotes" is an array and you should first unwind it before grouping it.
This works as expected:
db.mydata.aggregate([
{ $match: { "id": 44328 } } ,
{ $unwind: "$quotes" },
{ $group: { _id: "$quotes.id",
totalKey2:{ $sum: "$quotes.opportunityValues.Key2 Key2" },
totalKey3:{ $sum: "$quotes.opportunityValues.Key3 Key3 Key3" } }
}
]);