Create mind map of mongodb objects - node.js

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) => {

Related

How to bulk update the array of values in MongoDB?

I have an array of values to update with existing array. I tried with $set it throws a error. If I have an array of values to update it should update matching item in the array.
1.If I have following documents in a collection
table
[
{
"id": 1,
"name": "Fresh Mart",
"products": [
{
"name": "Onion",
"qty": 10,
"price": "85"
},
{
"name": "Tomato",
"qty": 10,
"price": "85"
}
]
},
{
"id": 2,
"name": "Alfred super market",
"products": [
{
"name": "Onion",
"qty": 10,
"price": "85"
},
{
"name": "Tomato",
"qty": 10,
"price": "85"
}
]
}
]
Following query updates one product in first document:
Query
db.collection.update({
"$and": [
{
id: 1
},
{
"products.name": "Onion"
}
]
},
{
"$set": {
"products.name": "Onion Updated"
}
})
I tried to update both products in the first document with following query:
db.collection.update({
"$and": [
{
id: 1
},
{
"products.name": [ "Onion", "Tomato" ]
}
]
},
{
"$set": {
"products.name": "Onion Updated"
}
})
db.collection.update({
id: 1
},
{
"$addToSet": {
"products": {
"$each": [
{
"name": "Onion Updated",
"qty": 10,
"price": "85"
},
{
"name": "Tomato Updated",
"qty": 10,
"price": "85"
}
]
}
}
})
I used $addToSet it will not allow the duplicates. Is possible to replace existing value with $addToSet
but it didn't update the document.
This answer is the dynamic approach of this answer https://stackoverflow.com/a/49870042/8987128
Configure your payload
let id = 1;
let updateName = [
{
oldName: "Onion",
newName: "Onion Updated"
},
{
oldName: "Tomato",
newName: "Tomato Updated"
},
];
Prepare arrayFilters and set conditions
let arrayFilters = [];
let set = {};
updateName.forEach((v, i) => {
arrayFilters.push({ ["p"+i]: v.oldName });
set["products.$[p"+i+"].name"] = v.newName;
});
Update query
db.collection.update(
{ id: id },
{ $set: set },
{ arrayFilters: arrayFilters }
)
Playground

I need to push into nested array in node mongoose

I used node mongoose.
I need to update this array push new item into Breackfast(mealList.foodList.breackfast || any),
I want to add new foodlist by time can you please give me suggestion for how to do,
{
"_id": "5fe43eb44cd6820963c98c32",
"name": "Monday Diet",
"userID": "5f225d7458b48d0fe897662e",
"day": "Monday",
"type": "private",
"mealList": [
{
"_id": "5fe43eb44cd6820963c98c33",
"time": "Breakfast",
"foodList": [
{
"_id": "5fe43eb44cd6820963c98c34",
"foodName": "Eggs",
"Qty": "2",
"calories": "calories",
"category": "category"
}
]
},
{
"_id": "5fe43eb44cd6820963c98c36",
"time": "Lunch",
"foodList": [
{
"_id": "5fe43eb44cd6820963c98c37",
"foodName": "food1",
"Qty": "100g"
},
]
}
],
"createdAt": "2020-12-24T07:09:40.141Z",
"updatedAt": "2020-12-24T07:09:40.141Z",
"__v": 0
}
I tried:
Diet.updateOne(
{ "Diet.mealList._id": req.body.mealId },
// { $push: { "Diet.0.mealList.$.foodList": req.body.foodList } },
{ $push: { foodList: req.body.foodList } }
)
Few Fixes:
convert your string _id to object type using mongoose.Types.ObjectId
remove Diet from first and push object in foodList
Diet.updateOne({
"mealList._id": mongoose.Types.ObjectId(req.body.mealId)
},
{
$push: {
"mealList.$.foodList": req.body.foodList
}
})
Playground

Create Mongoose Find and populate from multiple tables with property set

I'll be grateful for help with Mongoose. I have 3 tables: (Users)Table of users, (Animals)table of animals and table AnimalComments. So (AnimalComments)table reference users and animals.
const schemaComment = new mongoose.Schema({
userRef: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
animalRef: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Animal'
},
content: {
type: String
}
});
const schemaAnimal = new mongoose.Schema({
name: {
type: String
},
isCommentedByMe: {
type : Boolean,
default: false
},
commentCount: {
type : Number,
default: 0
}
});
What I want: I have animals. Users can add comment to animal. When user comment animal his comment is added to table AnimalComments where is stored userRef (userId), animalRef (animalId) and user comment text. Then in request response I want return all animals from table Animals but I need update property commentCount and isCommentedByMe based on value from table AnimalComments.
Response from table Animals:
{
"animals": [
{
"isCommentedByMe": false,
"commentCount": 0,
"name": "Jessica",
"userRef": {
"id": "5dc9bdf3dd5cae00177e184d"
},
"id": "5dcedd48368e9800176f2ef3"
}
]
}
Response from table Users:
{
"users": [
{
"name": "Jony Cash",
"id": "5dc9bdf3dd5cae00177e184d"
}
]
}
Response from table AnimalComments:
{
"comments": [
{
"userRef": "5dc9bdf3dd5cae00177e184d",
"animalRef": "5dcedd48368e9800176f2ef3",
"content": "Sample text"
}
]
}
I want result for exmaple:
{
"animals": [
{
"isCommentedByMe": true,
"commentCount": 4,
"name": "Jessica",
"userRef": {
"id": "5dc9bdf3dd5cae00177e184d"
},
"id": "5dcedd48368e9800176f2ef3"
}
]
}
You don't need to keep isCommentedByMe and commentCount fields in animal schema.
And you need to be able access comments from your animals. But in animal schema there is no field to make that connection. So we need to use virtual population.
So your animal schema must be like this:
const schemaAnimal = new mongoose.Schema(
{
name: {
type: String
}
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true }
}
);
// Virtual populate
schemaAnimal.virtual("comments", {
ref: "Comment", //must be changed to the name you used for Comment model.
foreignField: "animalRef",
localField: "_id"
});
Now, we can use the following code to populate the comments.
router.get("/animals", async (req, res) => {
const animals = await Animal.find({}).populate("comments");
res.send(animals);
});
This will give you a result like this:
[
{
"_id": "5dd66c73069f88614c12b394",
"name": "Animal 1",
"__v": 0,
"comments": [
{
"_id": "5dd66cfd069f88614c12b39a",
"userRef": "5dd66b54c5195127ec5a1b82",
"animalRef": "5dd66c73069f88614c12b394",
"content": "User 1 - Animal 1",
"__v": 0
},
{
"_id": "5dd66d30069f88614c12b39d",
"userRef": "5dd66b71c5195127ec5a1b83",
"animalRef": "5dd66c73069f88614c12b394",
"content": "User 2 - Animal 1",
"__v": 0
}
],
"id": "5dd66c73069f88614c12b394"
},
{
"_id": "5dd66c7d069f88614c12b395",
"name": "Animal 2",
"__v": 0,
"comments": [
{
"_id": "5dd66d09069f88614c12b39b",
"userRef": "5dd66b54c5195127ec5a1b82",
"animalRef": "5dd66c7d069f88614c12b395",
"content": "User 1 - Animal 2",
"__v": 0
}
],
"id": "5dd66c7d069f88614c12b395"
},
{
"_id": "5dd66c88069f88614c12b396",
"name": "Animal 3",
"__v": 0,
"comments": [
{
"_id": "5dd66d46069f88614c12b39e",
"userRef": "5dd66b71c5195127ec5a1b83",
"animalRef": "5dd66c88069f88614c12b396",
"content": "User 2 - Animal 3",
"__v": 0
}
],
"id": "5dd66c88069f88614c12b396"
}
]
To convert this result to what you wanted, we can use map like this:
Please note that, you need to set loggedInUserId variable to logged in user's id.
router.get("/animals", async (req, res) => {
const loggedInUserId = "5dd66b54c5195127ec5a1b82";
const animals = await Animal.find({}).populate("comments");
const result = animals.map(animal => {
return {
id: animal._id,
name: animal.name,
isCommentedByMe:
animal.comments.filter(c => c.userRef.toString() === loggedInUserId)
.length > 0,
commentCount: animal.comments.length
};
});
res.send(result);
});
The result will be like this:
[
{
"id": "5dd66c73069f88614c12b394",
"name": "Animal 1",
"isCommentedByMe": true,
"commentCount": 2
},
{
"id": "5dd66c7d069f88614c12b395",
"name": "Animal 2",
"isCommentedByMe": true,
"commentCount": 1
},
{
"id": "5dd66c88069f88614c12b396",
"name": "Animal 3",
"isCommentedByMe": false,
"commentCount": 1
}
]
And the answer for the question in the comments is: (how to reference User)
const animals = await Animal.find({}).populate({
path: "comments",
model: Comment,
populate: [
{
path: "userRef",
model: User
}
]
});
This will give you the userRef like this:
[
{
"_id": "5dd66c73069f88614c12b394",
"name": "Animal 1",
"__v": 0,
"comments": [
{
"_id": "5dd66cfd069f88614c12b39a",
"userRef": {
"_id": "5dd66b54c5195127ec5a1b82",
"name": "User 1",
"__v": 0
},
"animalRef": "5dd66c73069f88614c12b394",
"content": "User 1 - Animal 1",
"__v": 0
},
{
"_id": "5dd66d30069f88614c12b39d",
"userRef": {
"_id": "5dd66b71c5195127ec5a1b83",
"name": "User 2",
"__v": 0
},
"animalRef": "5dd66c73069f88614c12b394",
"content": "User 2 - Animal 1",
"__v": 0
}
],
"id": "5dd66c73069f88614c12b394"
},
{
"_id": "5dd66c7d069f88614c12b395",
"name": "Animal 2",
"__v": 0,
"comments": [
{
"_id": "5dd66d09069f88614c12b39b",
"userRef": {
"_id": "5dd66b54c5195127ec5a1b82",
"name": "User 1",
"__v": 0
},
"animalRef": "5dd66c7d069f88614c12b395",
"content": "User 1 - Animal 2",
"__v": 0
}
],
"id": "5dd66c7d069f88614c12b395"
},
{
"_id": "5dd66c88069f88614c12b396",
"name": "Animal 3",
"__v": 0,
"comments": [
{
"_id": "5dd66d46069f88614c12b39e",
"userRef": {
"_id": "5dd66b71c5195127ec5a1b83",
"name": "User 2",
"__v": 0
},
"animalRef": "5dd66c88069f88614c12b396",
"content": "User 2 - Animal 3",
"__v": 0
}
],
"id": "5dd66c88069f88614c12b396"
}
]

Add a new array in a nested document [duplicate]

I want add new data my nested array
My document is:
{
"username": "erkin",
"email": "erkin-07#hotmail.com",
"password": "b",
"playlists": [
{
"_id": 58,
"name": "asdsa",
"date": "09-01-15",
"musics": [
{
"name": "INNA - Cola Song (feat. J Balvin)",
"duration": "3.00"
},
{
"name": "blabla",
"duration": "3.00"
}
]
}
]
}
I want add music in this playlist section:
{
"username": "erkin",
"email": "erkin-07#hotmail.com",
"password": "b",
"playlists": [
{
"_id": 58,
"name": "asdsa",
"date": "09-01-15",
"musics": [
{
"name": "INNA - Cola Song (feat. J Balvin)",
"duration": "3.00"
},
{
"name": "blabla",
"duration": "3.00"
},
{
"name": "new",
"duration": "3.00"
}
]
}
]
}
Here is what I tried:
$users->update(
array(
'_id' => new MongoId (Session::get('id')),
'playlists._id' => $playlistId
),
array(
'$push' => array('playlists.musics' => array(
'name' => 'newrecord',
'duration' => '3.00'
))
)
);
Probably something like this where ID is your ObjectId. The first {} are necessary to identify your document. It is not required to use an ObjectId as long as you have another unique identifier in your collection.
db.collection.update(
{ "_id": ID, "playlists._id": "58"},
{ "$push":
{"playlists.$.musics":
{
"name": "test name",
"duration": "4.00"
}
}
}
)
This way it worked for me!
"playlists.$[].musics":
db.collection.update(
{ "_id": ID, "playlists._id": "58"},
{ "$push":
{"playlists.$[].musics":
{
"name": "test name",
"duration": "4.00"
}
}
}
)
https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/#position-nested-arrays-filtered
I suggest you using arrayFilters since it supports multiple nested documents and clearer.
db.collection.update(
{ "_id": ID},
{ "$push":
{"playlists.$[i].musics":
{
"name": "test name",
"duration": "4.00"
}
}
},
{
arrayFilters: [
{'i._id': 58,},
],
},
)
2022 update:
Full snippet:
from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017/', maxPoolSize=50)
db = client.name_of_db
collection = db["name_of_collection"]
To push:
collection.find_one_and_update(
{"_id": 'id_of_the_document'},
{"$push": {"key":"value"}})
To push into nested:
collection.find_one_and_update(
{"_id": 'id_of_the_document'},
{"$push": {"key.nested_key":"value"}})

mongoDB + Node update field from all documents

i need to update one field from a subdocument that appears inside other documents for all documents...
Something like this, i have some documents like this:
{
"_id": { "$oid" : "531cc2410b4ebf000036b2d7" },
"name": "ALMACEN 2",
"items": [
{
"_id": { "$oid" : "531ed4cae604d3d30df8e2ca" },
"brand": "BJFE",
"type": 0,
"color": "GDRNCCD",
"hand": 1,
"price": 500,
"model": 0,
"qty": 24
},
{
"_id": { "$oid" : "531ed4cae604d2330df8e2ca" },
"brand": "BJFE",
"type": 0,
"color": "GDRNCCD",
"hand": 1,
"price": 500,
"model": 0,
"qty": 12443
},
{
"_id": { "$oid" : "531ed22ae604d3d30df8e2ca" },
"brand": "BJFE",
"type": 0,
"color": "GDRNCCD",
"hand": 1,
"model": 0,
"price": 500,
"qty": 5
}
]
}
I want to set the qty to 0 for all documents that contains the item with _id 531ed22ae604d3d30df8e2ca
At moment i have it
warehouses.update({$.items._id: new ObjectID(item.brand._id)}, { $set: { qty: 0}}, {safe: false, multi: true}, function (e) {
if (e) {
error(e);
return;
}
success();
});
But i think the query is not ok..
You'll need to use the "$" positional operator to achieve this:
db.warehouses.update({"items._id": "531ed22ae604d3d30df8e2ca"}, { $set: { "items.$.qty":0} } )
In the mongo console it would look like this:
do {
db.warenhouse.update(
{
"_id" : "531cc2410b4ebf000036b2d7",
items: {
$elemMatch: {
qty: { $ne: 0 }
}
}
},
{
$set: {
"items.$.qty": 0
}
}
)
} while (db.getPrevError().n != 0);
Resource link

Resources