MongoDB - MongooseJS - Retrieve ID from parent to set child in collection - javascript

I am currently working in an app which the data is persisted in MongoDB using the MongooseJS.
There is a collection: DataRoomFolder. The documents have the following structure:
{
id,
isFolder,
name,
parentId: ObjectId(parent-DataRoomFolder),
projectId,
...
}
For each model Project, there is linked different DataRoomFolder but by default, when it is created, it will add a schema already predefined which I already have:
[
{
label: '01-Architecture',
items: [
{ label: 'Design', items: [] },
{ label: 'Drawings', items: [] },
],
},
{
label: '02-Permits',
items: [{ label: 'Building Permit', items: [] }],
},
{
label: '02-Permits',
items: [{ label: 'Building Permit', items: [] }],
},
{
label: '03-Control office',
items: [
{ label: 'Construction', items: [
{ label: 'Calculation reports', items: [] },
{ label: 'Datasheets', items: [] },
{ label: 'Drawings', items: [] },
{ label: 'Hand-over reports', items: [] },
{ label: 'Maintenance checklists', items: [] },
{ label: 'One-line diagrams', items: [] },
{ label: 'Technical notices', items: [] },
] },
{ label: 'Environment', items: [] },
{ label: 'Safety', items: [
{ label: 'Calculation reports', items: [] },
{ label: 'Datasheets', items: [] },
{ label: 'Drawings', items: [] },
{ label: 'Hand-over reports', items: [] },
{ label: 'Maintenance checklists', items: [] },
{ label: 'One-line diagrams', items: [] },
{ label: 'Technical notices', items: [] },
] },
],
}
As you can see, there is parent-children reference.
The question: when I loop for each to create every folder, I need to create have first the parent folder saved to get the ID and parsed to the children.
I saw a workaround with Populate but it does not fit my needs.
How should I persist to get parent IDs and carrying on adding to the children and saving and not getting affected the performance?
Thanks!

Related

Antd Tree, : how to Disable checking child by default

I working on a react project using Antd and I want to be able to disable cheking childs of my Tree component, so I can check only parent.This is my code
I found that I can add checkable : false to my child but I must create a function that render me a new TreeData that I can use instead of my normal data so I've tried this :
const TreeData = (data) => {
data.map((category) => {
category.children.map((family) => {
family.children.map((table) => {
table.checkable = false;
});
});
});
};
But it return undefined when i'm console.log the data received..
So my question is : how to switch from this :
const treeData = [
{
title: "0-0",
key: "0-0",
children: [
{
title: "0-0-0",
key: "0-0-0",
children: [
{
title: "0-0-0-0",
key: "0-0-0-0"
},
{
title: "0-0-0-1",
key: "0-0-0-1"
},
{
title: "0-0-0-2",
key: "0-0-0-2"
}
]
},
{
title: "0-0-1",
key: "0-0-1",
children: [
{
title: "0-0-1-0",
key: "0-0-1-0"
},
{
title: "0-0-1-1",
key: "0-0-1-1"
},
{
title: "0-0-1-2",
key: "0-0-1-2"
}
]
},
{
title: "0-0-2",
key: "0-0-2"
}
]
},
{
title: "0-1",
key: "0-1",
children: [
{
title: "0-1-0-0",
key: "0-1-0-0"
},
{
title: "0-1-0-1",
key: "0-1-0-1"
},
{
title: "0-1-0-2",
key: "0-1-0-2"
}
]
},
{
title: "0-2",
key: "0-2"
}
];
To this :
const treeData = [
{
title: "0-0",
key: "0-0",
children: [
{
checkable: false,
title: "0-0-0",
key: "0-0-0",
children: [
{
title: "0-0-0-0",
key: "0-0-0-0"
},
{
title: "0-0-0-1",
key: "0-0-0-1"
},
{
title: "0-0-0-2",
key: "0-0-0-2"
}
]
},
{
checkable: false,
title: "0-0-1",
key: "0-0-1",
children: [
{
title: "0-0-1-0",
key: "0-0-1-0"
},
{
title: "0-0-1-1",
key: "0-0-1-1"
},
{
title: "0-0-1-2",
key: "0-0-1-2"
}
]
},
{
checkable: false,
title: "0-0-2",
key: "0-0-2"
}
]
},
{
title: "0-1",
key: "0-1",
children: [
{
checkable: false,
title: "0-1-0-0",
key: "0-1-0-0"
},
{
checkable: false,
title: "0-1-0-1",
key: "0-1-0-1"
},
{
checkable: false,
title: "0-1-0-2",
key: "0-1-0-2"
}
]
},
{
title: "0-2",
key: "0-2"
}
];
Without hardchanging the first data of my Tree.
Thank you
This may be one possible implementation to set checkable as false for the specific nodes described in this question:
const makeUnCheckable = dataArr => (
dataArr.map(
obj => ({
...obj,
children: obj?.children?.map(cObj => ({
...cObj,
checkable: false
}))
})
)
);
return (
<Tree
checkable
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onCheck={onCheck}
checkedKeys={checkedKeys}
onSelect={onSelect}
selectedKeys={selectedKeys}
treeData={makeUnCheckable(treeData)}
/>
);
This is the result displayed on Codesandbox:
NOTES:
The elements showing as checked are clicked manually.
There is no check option for nodes 0-0-0, 0-0-1, 0-0-2, 0-1-0-0, 0-1-0-1, 0-1-0-2 - which is the expected objective defined in the question under To this
EDITED:
On perusing this previous question it seems like OP requires something like this:
(A tree where leaf nodes are uncheckable)
This may be achieved by a recursive method - something like this:
(Changes are present in: Lines 100 to 106. And line 118.)
EDITED - 2
Update based on comments below.
In order to identify the children for any given parent/key, something like the below may be useful:
Two methods are here. One is findKey which is recursive and gets the object which has a particular key (say 0-0-1). The other is to check if the object with the key has any children and if yes, return the children array.

Build nested array based on list of ancestors and depth value

I have category model referencing itself. Below is data of category in mongodb.
{ "_id":{"$oid":"5f55acc029d19e1ac402908f"},
"parents":null,
"name":"pizza",
"slug":"pizza",
"userID":"5f38c867b10f740e38b12198",
"ancestors":[],
}
{ "_id":{"$oid":"5f55b3c0a7b68b3bc0fe16c5"},
"parents":{"$oid":"5f55acc029d19e1ac402908f"},
"name":"premium",
"slug":"premium",
"userID":"5f38c867b10f740e38b12198",
"ancestors":[{
"_id":{"$oid":"5f55acc029d19e1ac402908f"},
"name":"pizza",
"parents":null,
"slug":"pizza",
"depth":{"$numberInt":"0"}
}],
}
{ "_id":{"$oid":"5f55b726b6b12042d09057c2"},
"parents":{"$oid":"5f55b3c0a7b68b3bc0fe16c5"},
"name":"peri peri chicken",
"slug":"peri-peri-chicken",
"userID":"5f38c867b10f740e38b12198",
"ancestors":[{
"_id":{"$oid":"5f55b3c0a7b68b3bc0fe16c5"},
"name":"premium",
"parents":"5f55acc029d19e1ac402908f",
"slug":"premium",
"depth":{"$numberInt":"1"}
},
{
"_id":{"$oid":"5f55acc029d19e1ac402908f"},
"parents":null,
"name":"pizza",
"depth":{"$numberInt":"0"},
"slug":"pizza"
}]
}
{ "_id":{"$oid":"5f55bb8be3088f473c4e15ac"},
"parents":null,
"name":"burger",
"slug":"burger",
"userID":"5f38c867b10f740e38b12198",
"ancestors":[]
}
I have following model in mongoose
const ItemCategorySchema = new Schema({
name: {
type: String,
required: true
},
slug: {
type: String,
index: true
},
parents: {
type: Schema.Types.ObjectId,
default: null,
ref: 'ItemCategory'
},
ancestors: [{
_id: {
type: Schema.Types.ObjectId,
ref: "ItemCategory",
index: true
},
name: { type: String },
parents: { type: String },
slug: { type: String },
depth: { type: Number }
}],
userID: {
type: String,
required: true
}
});
How can I build array like below using the information in ancestors and depth. I am using mongoose. Is there any function in mongoose to populate all category of self referencing into any number of level or depth?
const options = [
{ value: 'pizza', label: 'pizza',
options: [
{ value: 'premium', label: 'premium',
options: [
{ value: 'peri-peri-chicken', label: 'peri peri chicken' },
{ value: 'chicken-and-bacon', label: 'chicken and bacon'},
],
},
{ value: 'traditional', label: 'traditional',
options: [
{ value: 'beef-and-onion', label: 'beef and onion' },
],
},
],
},
{ value: 'burger', label: 'burger',
options: [
{ value: 'chicken', label: 'chicken' },
{ value: 'beef', label: 'beef' },
],
},
]

Flatten a deeply nested array with objects and arrays

I have an array of objects that contain another array with objects. The nesting is four levels deep.
The structure of the array is:
[
{
title: 'Title',
type: 'section',
links: [
{
label: 'Label',
id: 'id_1',
links: [
{
title: 'Title',
type: 'section',
links: [
{
label: 'Label',
id: 'id_2',
links: [
{
label: 'Label',
id: 'id_3',
links: [],
}
]
}
]
},
{
title: 'Other title',
type: 'section',
links: [
{
label: 'Label',
id: 'id_4',
links: [],
}
]
}
]
}
]
}
]
I want to have a flattened array with the id's of the link arrays that contain links (they are parents of submenu's).
So the desired outcome is like:
["id_1", "id_2"]
I have tried to get the outcome with this function taken from MDN:
flatDeep(arr, d = 1) {
return d > 0
? arr.reduce((acc, val) =>
acc.concat(Array.isArray(val.links)
? this.flatDeep(val.links, d - 1)
: val.links), [])
: arr.slice();
}
This gives me an empty array.
Use Array.flatMap(). Destructure each object and use an empty array as default for missing id values. Concat the id and the result of flattening the links recursively.
const flattenIds = arr => arr.flatMap(({ id = [], links }) =>
[].concat(id, flattenIds(links))
);
const data = [{ title: 'Title', type: 'section', links: [{ label: 'Label', id: 'id_1', links: [{ title: 'Title', type: 'section', links: [{ label: 'Label', id: 'id_2', links: [{ label: 'Label', id: 'id_3', links: [] }] }] }, { title: 'Other title', type: 'section', links: [{ label: 'Label', id: 'id_4', links: [] }] }] }] }];
const result = flattenIds(data);
console.log(result);
You could get a flat array with a recursion and a check for id for missing property.
const
getId = ({ id, links }) => [
...(id === undefined ? [] : [id]),
...links.flatMap(getId)
],
data = [{ title: 'Title', type: 'section', links: [{ label: 'Label', id: 'id_1', links: [{ title: 'Title', type: 'section', links: [{ label: 'Label', id: 'id_2', links: [{ label: 'Label', id: 'id_3', links: [] }] }] }, { title: 'Other title', type: 'section', links: [{ label: 'Label', id: 'id_4', links: [] }] }] }] }],
result = data.flatMap(getId);
console.log(result);
Here is a non-recursive version.
const data = [{title:'Title',type:'section',links:[{label:'Label',id:'id_1',links:[{title:'Title',type:'section',links:[{label:'Label',id:'id_2',links:[{label:'Label',id:'id_3',links:[]}]}]},{title:'Other title',type:'section',links:[{label:'Label',id:'id_4',links:[]}]}]}]}];
const stack = data.slice();
const result = [];
let obj;
while (obj = stack.shift()) {
if ("id" in obj && obj.links.length > 0) result.push(obj.id);
stack.push(...obj.links);
}
console.log(result);
This uses breath first, but can easily be changed into depth first. You'll only have to change the stack.push call into stack.unshift.
For a more detailed explanation about the two, check out Breadth First Vs Depth First.
var array = JSON.parse('[{"title":"Title","type":"section","links":[{"label":"Label","id":"id_1","links":[{"title":"Title","type":"section","links":[{"label":"Label","id":"id_2","links":[{"label":"Label","id":"id_3","links":[]}]}]},{"title":"Other title","type":"section","links":[{"label":"Label","id":"id_4","links":[]}]}]}]}]');
arr = [];
while(array.length != 0) {
var ob1 = array.splice(0,1)[0];
for(var ob2 of ob1.links) {
if (ob2.links.length !== 0) {
arr.push(ob2.id);
array = array.concat(ob2.links);
}
}
}
console.log(arr);
Here's the output as you requested:
[
"id_1",
"id_2"
]
I think recursive function will simplify. (recursively look for lists array and push the id into res).
const data = [
{
title: "Title",
type: "section",
links: [
{
label: "Label",
id: "id_1",
links: [
{
title: "Title",
type: "section",
links: [
{
label: "Label",
id: "id_2",
links: [
{
label: "Label",
id: "id_3",
links: []
}
]
}
]
},
{
title: "Other title",
type: "section",
links: [
{
label: "Label",
id: "id_4",
links: []
}
]
}
]
}
]
}
];
const res = [];
const ids = data => {
data.forEach(item => {
if ("id" in item) {
res.push(item.id);
}
if (item.links) {
ids(item.links);
}
});
};
ids(data);
console.log(res);

filter nested object array in javascript

I want to sort the above given array so that the output array will be as given in output section. I have tried some code which is given below. I am using javascript for sorting. In angular I am using this To display menu according to user role.
I have googled a lot but not getting solution
this.items = [
{
label: 'Home', routerLink: ['Home']
},
{
label: 'menu1',
items: [
{
label: 'submenu1',
routerLink: '/submenu1'
},
{
label: 'submenu2'
, routerLink: '/submenu2'
},
{
label: 'submenu3',
routerLink: ['/submenu3']
}
]
},
{
label: 'menu2',
items: [
{
label: 'submenu5',
routerLink: ['/submenu5']
},
]
},
{
label: 'menu3',
items: [
{
label: 'submenu6',
routerLink: ['/submenu6'],
}
]
},
];
output:
this.items = [
{
label: 'Home', routerLink: ['Home']
},
{
label: 'menu1',
items: [
{
label: 'submenu1',
routerLink: '/submenu1'
}
]
},
{
label: 'menu3',
items: [
{
label: 'submenu6',
routerLink: ['/submenu6'],
}
]
},
];
code for sorting:
let filterArr = this.filteredArray
.filter(x => x.label == "Home" && x.label == "menu3")
.map(y => y.items.filter(z => z.label == 'submenu6'));
You could move the wanted menu and submenu labels into arrays and filter the objects by creating new object with filtered menus.
This approach does not mutate the data.
It uses Array#flatMap for the outer array and Array#filter for getting the wanted parts of the nested array.
If the nested array does not have any item, then take the original object.
var items = [{ label: 'Home', routerLink: ['Home'] }, { label: 'menu1', items: [{ label: 'submenu1', routerLink: '/submenu1' }, { label: 'submenu2', routerLink: '/submenu2' }, { label: 'submenu3', routerLink: ['/submenu3'] }] }, { label: 'menu2', items: [{ label: 'submenu5', routerLink: ['/submenu5'] }] }, { label: 'menu3', items: [{ label: 'submenu6', routerLink: ['/submenu6'] }] }],
menu = ['Home', 'menu1', 'menu3'],
submenu = ['submenu1', 'submenu6'],
result = items.flatMap(o => {
if (!menu.includes(o.label)) return [];
var items = (o.items || []).filter(({ label }) => submenu.includes(label));
return items.length ? { ...o, items } : o;
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

java script filling up an array so it matches following structure

lets say i want to start with empty value of variable data, how can to achieve this result with javascript using push method:
var data = [
{
label: 'node1',
children: [
{ label: 'child1' },
{ label: 'child2' }
]
},
{
label: 'node2',
children: [
{ label: 'child3' }
]
}
];
i have tried:
data.push("label: nodel", "children:"+ ['child1', 'child2']);
looking at code above i need to insert one element that will be linked with list of childs. Can someone help me achieve this.. i would be very grateful.
Best regards.
Is this what you mean?
var object1 = {
label: 'node1',
children: [
{ label: 'child1' },
{ label: 'child2' }
]
};
var data = new Array();
data.push(object1);
OR
data.push({ label: 'node1', children: [ { label: 'child1' }, { label: 'child2' } ] });
EDITED TO SHOW YOSHIS VERSION ASWELL

Categories

Resources