Use mongoose query to get only a subdocument - javascript

I have a similar datastructure as this:
var GrandGrandChild = mongoose.Schema({
attribute: String,
id: Number
});
var GrandChild = mongoose.Schema({
children: [GrandGrandChild],
id: Number,
irrelevantAttribute: String
});
var Child = mongoose.Schema({
children: [GrandChild],
id: Number,
irrelevantAttribute2: String
});
var Parent = mongoose.Schema({
children: [Child],
id: Number,
irrelevantAttribute3: String
});
var GrandParent = mongoose.Schema({
children: [Parent],
id: Number,
irrelevantAttribute4: String
});
These are a lot of collections with subdocuments in them.
Note that the ID's are unique to their siblings, but not unique to all elements with that same schema.
So one grand parent can have an parent with id 0, and another grandparent can also have a parent with id 0. but one grandparent can not have 2 parents with id 0.
The only schema that gets saved is the GrandParent schema, and mongoose/mongodb makes a nice big single document of all the data of this grandparent. (Exactly what i am looking for)
So here is my issue: I have a GrandParent ID, Parent ID, Child ID, GrandChildID and GrandGrandChild ID, and i want to somehow get only the GrandGrandChild object to which all these ID's are pointing.
The ugly way would be to, but currently the only way i can get to work, is to make a query that gets this big document of GrandParent, and manually loop through all arrays to find the right Parent, then loop again to find the right child, then loop again to find the right grandchild, then loop again and find the grandgrandchild im needing here.
My question is, how would i compose a query in mongoose that either returns only the grandgrandchild document, or the grandparent document with only the children attribute included, and in that children attribute only the parent object included that refers to the child object that refers to the grandchild object that refers to the grandgrandchild object, allowing the following with the result:
GRANDPARENT PARENT CHILD GRANDCHILD GRANDGRANDCHILD
grandparent.children[0].children[0].children[0].children[0].attribute;
I hope someone can help me on this query, as far is i got is this:
GrandParentModel.findOne(
{
"id" : 0,
"children.id" : 0,
"children.children.id" : 0,
"children.children.children.id" : 0,
"children.children.children.children.id" : 0
},
{"children.children.children.children.$" : 1}, callback);
The problem with this query is that the unnessicary siblings arent trimmed away.
I hope someone can help me out.
Hylke Bron

it has been some time since I asked this question, but I think I found a rather elegant way of working with these kind of structures.
In this case I'll show how it works with only GrandParent, Parent and Child.
Instead of storing a list of subdocuments in each document (GrandParent.children, Parent.children), I created an unique identifier of the following structure:
Child.referenceId = {
grandparent: "some key to the grandparent of the parent",
parent: "some key to the parent",
child: "key of this child"
};
Parent.referenceId = {
grandparent: "some key to its grandparent",
parent: "key of this parent"
}
GrandParent.referenceId = {
grandparent: "key of this parent"
}
This creates a hierarchy of GrandParent > Parent > Child.
The models would be something like the following:
var idStructure = {
grandparent: { type: String, required: true },
parent: { type: String, required: false },
child: { type: String, required: false }
};
var GrandParent = mongoose.Schema({
id: idStructure,
irrelevantAttribute: String
});
var Parent = mongoose.Schema({
id: idSructure,
irrelevantAttribute: String
});
var Child = mongoose.Schema({
id: idStructure,
irrelevantAttribute: String
});
Notice that a Parent doesnt directly knows its parent, for they are not stored as subdocuments. Yet there still is a connection between Parent and Child, through the referenceId.
When searching for the whole familytree of a GrandParent, one would simply execute 3 queries, and then connect them correctly:
// First find all children which belong to the grandparent
Child.find({"id.grandparent" : "some key to the grandparent"})
.exec(function(err, children)
{
if(err)
return;
Parent.find({"id.grandparent" : "some key to the grandparent"})
.exec(function(err, parents)
{
if(err)
return;
// Loop through the parents and children to connect them before returning to a client
for(var i = 0; i < parents.length; i++)
{
var parent = parents[i];
parent.children = [];
// loop through the children to check if they belong to the current parent
for(var j = 0; j < children.length; j++)
{
var child = children[j];
if(parent.id.parent == child.id.parent)
parent.children.push(child);
}
}
// After filling the children into the parents, get the grandparents and do the same for the parents and grandparents as done for the children and parents.
GrandParent.find({"id.grandparent" : "some key to the grandparent"})
.exec(function(err, grandparents)
{
// TODO: the same as done above (two loops, one loops the grandparents, other loops the parents
// Once this is finished, we have a filled grandparent
});
});
});
The code above would result in just ONE grandparent, filled with parents, which are filled with children.
The reason no more grandparents would be found is because the id of the grandParent should be unique, for the referenceId of the grandparent only has a grandparent property.
I hope i made my point clear, because through this method, one can easily search for one specific child, easily get its parent through the reference id, and its grandparent also through the reference id.
It might be a bit complex, but once you figure the method out for yourself, its all kinda straight forward.
Hylke

Is very difficult to get this kind of things work in a clean way.
I didn't find a clean solution on this topic, but maybe I can help you with the looping thing.
You can avoid the loop using:
var doc = parent.children.id(id); Finding a sub-document
I hope this help you.
Regards,
Sebastian.

this worked for me
model.find({_id:args.id},{ commentList: { $elemMatch: { _id: todo.commentList[todo.commentList.length-1] } } },(err, todos) => {
if (err) reject(err)
else resolve(todos)
console.log(todos);
})
$elemMatch (projection)

Related

How to pass values from Child Array to a Parent

My requirement is as follows
In parent component, i am passing an array of Child Components(array can be 1 or more than 1)
As the image shows, a child component consists of elements like, input[type=range], input[type=number], dropdown menu, etc
Parent component has a button
<button>Search Location</button>
When I click on Search button in Parent, I need the value of every single elements in each Child Component,
for eg. structure can be as follows
let finalObj={
child1: {
dropValue: "Room1",
cond: "AND"
},
child2: {
inputVal: 50,
cond: "OR"
},
child[n]: {
rangeVal: 1,
cond: ""
}
}
Also, we can change the value again(before clicking search), and Search button should always pickup, the current set value of each component.
I am not sure how to go ahead with this. Any pointers will be really helpful. Please help
So you need to change an array of components into an object of... well first of all that's a .reduce use.
const almostFinalObj = components.reduce((retval, each, i) => {
retval['child'+i] = each;
return retval;
}, {});
That will give an object like
almostFinalObj = {
child1: component1,
child2: component2,
childN: componentN,
}
Now we can .forEach through it, transforming each child component into whatever format you're looking for. (I'm unclear on that part but maybe you can figure out the rest.)
Object.keys(almostFinalObj).forEach((each, i, array) => {
let component = array[i];
array[i] = {};
component.querySelectorAll('input').forEach(e => {
array[i][e.name] = e.value;
});
});
This assumes the name attribute exists on each element in each child row-component. (As in, each radio button in your example has <input type='radio' name='cond' .... />.) You could use id or even a data-XXX attribute as well instead of e.name.

Nesting related elements together parent/child

Let's say I have an array as so with objects:
[
{
"id": "5a97e047f826a0111b754beb",
"name": "Hogwarts",
"parentId": "5c7bf2191d41c810b2ad6186",
"childrenIds": []
},
{
"id": "5c7bf2191d41c810b2ad6186",
"name": "Defense Against The Dark Arts",
"parentId": null,
"childrenIds": [
"5a97e047f826a0111b754beb"
]
}
]
What I'd like to do is a function that returns another array, but this time with only items that don't have a parentID as root elements, and have a children array on them containing their children, so on reaching leaves who have an empty childrenIDs array. (also remove the parent/children id properties)
For the previous input I'd return something like this
[
{
"id": "5c7bf2191d41c810b2ad6186",
"name": "Defense Against The Dark Arts",
"children": [
{
"id": "5a97e047f826a0111b754beb",
"name": "Hogwarts"
}
]
}
]
I can't seem to think of any efficient code for this task, can anyone help?
You could keep a reference to each node by ID in an object and build the tree while you go. Since we may encounter references to entries that we didn't see yet, we'll create stubs in the meantime (consisting only of a children array) and add the rest of their fields later on.
This way we have only a single loop.
It seems you have sort of a vertical double-linked list, saving both the parent ID in the children and the children's IDs in the parent, but we need only one of those to build the tree. We'll use the parent ID stored in each child. (Note that this assumes that your structure is consistent without imbalanced relations or dangling references.)
For simplicity, we create one root node whose children we'll return at the end, so that we don't have to handle the nodes without parent any differently.
Then you could write code like this:
function makeTree (rows) {
// Use a symbol to denote the root ID to avoid clashes with any real IDs
const ROOT = Symbol('ROOT')
// Node index, by ID
// Add a root node from the start
const nodes = { [ROOT]: { children: [] } }
// Helper function to return an existing node or create a stub if not existing
const getNodeOrCreateStub = id => nodes[id] || (nodes[id] = { children: [] })
for (const row of rows) {
// Add current row to index, merging data with existing stub, if any.
// This keeps any existing references in other nodes' children arrays intact
// because Object.assign mutates the first object passed to it and returns the
// same object.
const node = Object.assign(getNodeOrCreateStub(row.id), row)
// Remove unwanted properties.
delete node.parentId
delete node.childrenIds
// Get parent or create parent stub if parent node not already existing
const parent = getNodeOrCreateStub(row.parentId || ROOT)
// Add current node as child to parent
parent.children.push(node)
}
// Return children of root node
return nodes[ROOT].children
}
Note that this code currently also creates empty children arrays in the leaf nodes, differently from your example above. However I think this makes the code simpler because it doesn't have to handle leaf nodes any different, neither in creating the tree nor in reading it later! (You wouldn't have to do children && children.length to check for children, you could always just access children directly.)
To change that and have the result exactly as in your example, you'd change the code as follows:
// Change this...
const getNodeOrCreateStub = id => nodes[id] || (nodes[id] = { children: [] })
// ...to this:
const getNodeOrCreateStub = id => nodes[id] || (nodes[id] = {})
// Also change this...
parent.children.push(node)
// ...to this:
if (!parent.children) parent.children = []
parent.children.push(node)
// ...or to this:
parent.children = [...parent.children || [], node]

How to delete references to children of deleted properties

When I delete an object property, I need to remove all references to descendant properties scattered around my application:
const people = {
mary: {
children: [
john: new ChildClass(),
paul: new ChildClass(),
george: new ChildClass()
]
},
homer: {
children: [
bart: new ChildClass(),
lisa: new ChildClass(),
maggie: new ChildClass()
]
}
};
const all_children = [];
/* pseudocode:
foreach people(children) {
foreach children(child) {
all_children.push(child);
}
}
*/
all_children.forEach(child => {
/* does something with mary and homer's children */
});
//////////////////
delete people.mary;
all_children.forEach(child => {
/* STILL does something with mary and homer's children,
even though mary has been deleted */
});
So I either need a way to delete all references to descendant properties, or I need a way to indicate that the reference is tied to a deleted parent and ignore it in my calculations.
What is the proper/most maintainable way to accomplish this.
Unless you really need to keep all the children in one array for performance reasons, the easier solution is to scrap the all_children array in favor of a function that returns an array of all children, like
function getAllChildren () {
var all_children = [];
/* pseudocode:
foreach people(children) {
foreach children(child) {
all_children.push(child);
}
}
*/
return all_children;
}
This way, if you delete Mary or Homer, calling this function will automatically reflect the deletion.
If I understand the question correctly, you're afraid of the children objects still exist in memory after you remove parent object from the object array. This isn't the case for javascript, because the language doesn't implement true classes. And therefore each copy of the new ChildClass() is passed by value and not reference.
Therefore you don't need to keep track of the children references in order to clean them up.

get all children of specific parent in node js

I want to get all children of specific parent. I am new to node.js and not able to write recursive function to do this task
var roots = [1,2,6];
var documents = [
{
"parent_id":1
,childerens:[4,5]
}
,{
"parent_id":4
,childerens:[9]
}
,{
"parent_id":9
,childerens:[]
}
,{
"parent_id":5
,childerens:[3]
}
,{
"parent_id":3
,childerens:[]
}
]
roots.forEach(function (rootParentId) {
var allchilderens=getAllchild(rootParentId);
})
Here's an example of what I mean:
var allchilderens = getAllchild(1);
allchilderens == [4,5,9,3]
function findAllChildren(element,is_root,childerens) {
if(is_root==false&&element!=undefined){
childerens.push(element);
}
var doc = documents.find(o => o.parent_id === element);
if(doc["childerens"].length==0){
return [];
}
else{
doc["childerens"].forEach(function (element) {
findAllChildren(element,false,childerens);
})
}
}
var childerens=[];
console.log(findAllChildren(1,true,childerens));
console.log("childerens==>",childerens);
You really want to build a tree structure here as step one.
Each object should, instead of containing a list of children ids, it should contain the actual children. That way, when you want to get all children, you can traverse that tree.
You probably should consider revising your data structure here. Why not use an object, where the user id is the key (since it should be unique). Then you would just need to get those keys directly.
Having everything inside of an array is potentially bad, since you'd have to iterate through the array to find each child, which would have (at worst), a run time equal to the length of the array.

Pushing data into an array of objects

Alright, I've got this blank array of objects.
I am dynamically finding every node in a web page and each node is going to have it's own object and properties.
I need a way to throw the values I need into their respective objects property
So, for example, I find the body node. I now have a special little object for this node. I need to throw pretty much everything about this little guy into his object's properties.
So I pretty much need it to render like this:
Turning this:
<html>
<body style="margin:0; padding:0;" title="My Title">
<p>some text</p>
<div class="wrapper"></div>
<footer></footer>
</body>
</html>
Into this:
this.nodesInfo = [ // All nodes in the page's DOM
{
type: 'body', // ex: body, section, aside, div, etc.
id: 'myID', // the Id of that element
class: ['myClass1', 'myClass2'], // the class/class' of that element
depth: '2', // the level in the page's DOM in which that element sits, this will be an integer
parent: 'html', // that elements direct parent Node
children:['div.wrapper', 'p', 'footer'], // any child Nodes that, that element may be a parent to
text: '', // the text inside that element if any exists
attributes: ["style=margin:0; padding:0;", "title='My Title'"] // all attributes of this node
}
];
It would of course cycle through each node it discovered and do this for each node accordingly, until it ran out of nodes.
The class, children, and attributes properties are arrays for the simple possibility of multiples of any of these. Everything else is just a string since a node can't have more than one ID, title, or direct parent tag.
If a node does not contain some of these properties then that property would remain blank/null/undefined.
My question is simple. Is this possible, if not would I instead have to create each object individually and the push them into my nodesInfo array?
I think the easiest way to go about this would be making an object of each Node and then pushing them all (once they are all created) into an array.
I was building something like this the other night. This should work and you can add more stuff easily. http://jsfiddle.net/elclanrs/UHbMa/
$.fn.buildTree = function() {
var tree = {};
this.find('*').andSelf().each(function(i, v) {
var parents = $(this).parents().length - 1,
depth = 0;
while (depth < parents) {
depth++;
}
tree[v.tagName.toLowerCase() + '(' + i + ')'] = {
id: (v.id) ? '#' + v.id : '',
className: (v.className) ? '.' + v.className.replace(' ', '.') : '',
depth: depth
};
});
return tree;
};
// Then you can do this...
var tree = $('#element').buildTree();
for (var tag in tree) {
// Get your variables
var tag.match(/\w+/), // Get rid of `(n)`
id = tree[tag].id,
className = tree[tag].className,
depth = tree[tag].depth;
html = 'something';
// Bla bla
}

Categories