Update many sub of sub-document array using $set / $push - node.js

Following is the Sample Workspace document.I want to update box positions when we drag and drop at front end.
{
"_id": ObjectId("5eaa9b7c87e99ef2430a320b"),
"logo": {
"url": ".../../../assets/logo/dsdsds.png",
"name": "testUpload"
},
"name": "My World",
"sections": [{
"box": [{
"_id": ObjectId("5da87b33502d6c634b3aa7ce"),
"name": "Meran To",
"position": 0
},
{
"_id": ObjectId("5da87b33502d6c7d873aa7d0"),
"name": "Documentation",
"position": 2
},
{
"_id": ObjectId("5da87b33502d6cdbb93aa7cf"),
"name": "File Manager Upload File Drive",
"position": 1
},
{
"_id": ObjectId("5da87b33502d6c276a3aa7cd"),
"name": "File Manager Upload File Drive",
"position": 1
}
],
"id": 1,
"title": "Simplicity",
"description": "Follow your barriers"
},
{
"box": [],
"id": 2,
"title": "xfxdfxcx 34",
"description": "sdsdsd sfsfsd ewrewrewre"
}
]
}
I send the updated positions from front-end to back-end via API, in an array as shown below.
[
{
"id": "5da87b33502d6c634b3aa7ce",
"position": 0
}, {
"id": "5da87b33502d6c7d873aa7d0",
"position": 1
}, {
"id": "5da87b33502d6cdbb93aa7cf",
"position": 2
}, {
"id": "5da87b33502d6c276a3aa7cd",
"position": 3
}]
I am currently updating DB using the below code
for (const el of req.body) {
await this.model.updateOne({
_id: req.params.workspaceId,
sections: {
$elemMatch: {
id: req.params.sectionId
}
},
'sections.box': {
$elemMatch: {
_id: el.id
}
},
}, {
$set: {
'sections.$[outer].box.$[inner].position': el.position
}
}, {
arrayFilters: [{
'outer.id': req.params.sectionId
}, {
'inner._id': el.id
}],
upsert: false,
});
}
But this is not the best method, it hits DB multiple times.
so I need to optimize this code with mongoose query itself.
May be using $set / $push.I don't know any exact methods.
So basically we need to remove the external for loop and make it work with mongoose itself.This is my requirement.
Thanks in advance for all the support.

There are 2 methods for doing it.
bulkWrite
const bulkOps = [];
req.body.forEach((el) => {
const upsertDoc = {
updateOne: {
filter: {
_id: req.params.workspaceId,
sections: {
$elemMatch: {
id: req.params.sectionId
}
},
'sections.box': {
$elemMatch: {
_id: el.id
}
},
},
update: {
$set: {
'sections.$[outer].box.$[inner].position': el.position
}
},
arrayFilters: [{
'outer.id': req.params.sectionId
}, {
'inner._id': el.id
}],
upsert: false,
}
};
bulkOps.push(upsertDoc);
});
const result = await this.model.collection.bulkWrite(bulkOps);
bulkUpdate
const bulkUpdate = this.model.collection.initializeUnorderedBulkOp();
req.body.forEach((el) => {
bulkUpdate.find({
_id: req.params.workspaceId,
sections: { $elemMatch: { id: req.params.sectionId } },
'sections.box': { $elemMatch: { _id: el.id } },
}).arrayFilters([{ 'outer.id': req.params.sectionId }, { 'inner._id': el.id }])
.updateOne({ $set: { 'sections.$[outer].box.$[inner].position': el.position }
});
});
await bulkUpdate.execute();

Related

Node js mongoose filter data from array in collection

I am having a response like this
....................................................................................................................................................................................................
{
"data": [
{
"user": "83k13bde05f40640j12075w",
"products": [
{
"type": "shoes",
"amount": 20
},
{
"type": "trousers",
"amount": 6
}
],
"inStock": false
},
{
"user": "9dc3f7de05f40640j12075y",
"products": [
{
"type": "chairs",
"amount": 11
},
{
"type": "bags",
"amount": 16
}
],
"inStock": false
},
{
"user": "6wb3f7ne35f40640m62p2gd",
"products": [
{
"type": "phones",
"amount": 2
},
{
"type": "clothes",
"amount": 15
}
],
"inStock": false
}
]
}
This the function outputting the above response
exports.getProducts = async (req,res) => {
const result = await Products
.find({inStock: false})
.select("-_id -createdAt -__v")
.exec()
if(!result) return res.status(400).json({ data: 'No product found' });
if(result.err) return res.json({ err: err });
return res.json({data: result});
}
But I want to get only products with the amount greater than 10
So my output should be like this
{
"data": [
{
"user": "83k13bde05f40640j12075w",
"products": [
{
"type": "shoes",
"amount": 20
}
],
"inStock": false
},
{
"user": "9dc3f7de05f40640j12075y",
"products": [
{
"type": "chairs",
"amount": 11
},
{
"type": "bags",
"amount": 16
}
],
"inStock": false
},
{
"user": "6wb3f7ne35f40640m62p2gd",
"products": [
{
"type": "clothes",
"amount": 15
}
],
"inStock": false
}
]
}
I tried using
.find({'products.amount': { $gt: 10 }})
But It didn't filter out the response
Did you try the $elemMatch operator ?
const result = await Products
.find({
inStock: false,
products: { $elemMatch: { amount: { $gt: 10 } } }
})
.select("-_id -createdAt -__v")
.exec();
You can use aggregation to achieve this.
First of all you use $match operator to find the documents with items in the array that match with your criteria. Then you can use $project and $filter operator to return the array filtered.
const result = await Products.aggregate([
{
"$match" : {
"products" : {
"$elemMatch" : { amount: { $gt: 10 } }
},
}
},
{
$project: {
user: 1,
inStock: 1,
products: {
$filter: {
input: "$products",
as: "products",
cond: { $gt: ["$products.amount", 10] }
}
}
}
}
])
.select("-_id -createdAt -__v")
.exec();
For further reading: https://studio3t.com/knowledge-base/articles/filter-elements-from-mongodb-arrays/#how-to-use-filter-and-project

Posting an aggregation as a new document/collection

In my client I have a form that is sent and stored in Mongo. Made an aggregation to get the name of the people that selected a same place, date and time. Now I would like to create a Mongo document containing all matches as collections so whenever there is a match in place, date and time of people you can get it in a collection. This is what I have so far:
router.get('/match', async (req, res) => {
const matchs = await Forms.aggregate([
{
$group: {
_id: { Date: "$date", Time: "$time", Place: "$place" },
Data: { $addToSet: {Name: "$firstName", Surname:"$surname"}},
count: { $sum: 1 }
}
},
{
$match: {
count: { $gte: 2}
}
},
]);
res.json(matchs)
});
This is the result that I would like to store in Mongo:
{
"_id": {
"Date": "2022-04-20",
"Time": "15:00",
"Place": "Mall"
},
"Data": [
{
"Name": "Carl",
"Surname": "Man"
},
{
"Name": "Christian",
"Surname": "Max"
}
],
"count": 2
}
{
"_id": {
"Date": "2022-04-20",
"Time": "13:00",
"Place": "Restaurant"
},
"Data": [
{
"Name": "Felix",
"Surname": "Sad"
},
{
"Name": "Liu",
"Surname": "Lam"
}
],
"count": 2
}
You can use $out as the last stage in your pipeline. In the following example, matching_collection will contain the result of your pipeline.
{ $out : "matching_collection" }
https://www.mongodb.com/docs/v4.2/reference/operator/aggregation/out/
You can also check $merge, it could be helpful as well.

Removing the specific object from array in mongoose and update it

Hi i am new in mongoose and mongodb. I want to remove specific object from the Array in my document and return the updated document. I have tried a lot but it always return null. Here is my document structure.
{
"role": "Student",
"skills": [
"html",
"css",
"js"
],
"_id": "5ef583198e9b23cc8c606c10",
"user": "5ee5c9ef26333935647e54bc",
"__v": 24,
"status": "Intern",
"education": [],
"internships": [
{
"current": false,
"_id": "5ef894d48f601512340f25b5",
"title": "Web",
"company": "asdfadfd",
"from": "2010-02-04T00:00:00.000Z"
},
{
"current": false,
"_id": "5ef894f31dc9413bf89c44d8",
"title": "Django",
"company": "example",
"from": "2010-02-04T00:00:00.000Z"
}
]
}
And here is my updating function
exports.deleteStudentInternship = async (req, res, next) => {
const deleteInternship = await Student.findOneAndUpdate(
{ $and: [{ user: req.user.id }, { 'internships': { $elemMatch: { _id: req.params.intern_id } } }] },
{ '$pull': { 'internships': { _id: req.params.intern_id } } },
{
new: true,
useFindAndModify: false,
},
function (error) {
if (error) return validationError(404, { internship: 'Internship id not exist' }, next)
}
);
if (!deleteInternship) {
return validationError(404, { internship: 'Internship id not exist' }, next)
}
res.status(200).json(deleteInternship);
}
Please change the pull part I mean
{ '$pull': { 'internships': { _id: req.params.intern_id } } }
to this and try:
{ '$pull': { 'internships': req.params.intern_id } }

GraphQL Query - cannot see nested elements in JSON respone

if anyone can hazard a guess or where to look it would be greatly appreciated.
I can get nested data when I run using graphgl API, however, from my node program it only shows top-level items - does not display the nested elements for the customer and lineitem object.
I am using Koa middle where, with promise response:
router.get('/orders/', async (ctx) => {
const auth = prepareAuth(ctx);
await getOrders(auth).then(response => ctx.body = response.data.data.orders);
console.log(ctx.body.edges)
However from the console it has (customer null and 'object':
[
{
node: {
createdAt: '2020-02-24T12:53:20Z',
customer: null,
name: '#1001',
lineItems: [Object]
}
},
{
node: {
createdAt: '2020-02-24T12:53:50Z',
customer: null,
name: '#1002',
lineItems: [Object]
}
},
{
node: {
createdAt: '2020-03-10T21:11:04Z',
customer: null,
name: '#1003',
lineItems: [Object]
}
}
]
when i use the GraphQL API directly the query works fine and I get full response:
{
"data": {
"orders": {
"edges": [
{
"node": {
"createdAt": "2020-02-24T12:53:20Z",
"customer": {
"displayName": "franko girl"
},
"name": "#1001",
"lineItems": {
"edges": [
{
"node": {
"name": "dance mat red",
"quantity": 4
}
}
]
}
}
},
{
"node": {
"createdAt": "2020-02-24T12:53:50Z",
"customer": {
"displayName": "franko man"
},
"name": "#1002",
"lineItems": {
"edges": [
{
"node": {
"name": "dance mat black",
"quantity": 2
}
}
]
}
}
},
{
"node": {
"createdAt": "2020-03-10T21:11:04Z",
"customer": {
"displayName": "franko man"
},
"name": "#1003",
"lineItems": {
"edges": [
{
"node": {
"name": "dance mat black",
"quantity": 1
}
},
{
"node": {
"name": "dance mat red",
"quantity": 1
}
}
]
}
}
}
]
}
},
Okay, so finally figured this out, for anyone else who stumbles accross this problem, you need to the convert the json object to a string using built in javascript function: JSON.stringify()
from W3schools.com
var obj = { name: "John", age: 30, city: "New York" };
var myJSON = JSON.stringify(obj);

Find and remove by Id a subdocument in array with mongoose

I'm trying to delete a subdocuments in array with Mongoose.
My datas :
{
"_id": {
"$oid": "5d88dfe45feb4c06a5cfb762"
},
"spaces": [{
"_id": {
"$oid": "5d88dfe45feb4c06a5cfb76f"
},
"name": "Building 2",
"subSpace": [{
"_id": {
"$oid": "5d88dfe45feb4c06a5cfb771"
},
"name": "Basement"
}, {
"_id": {
"$oid": "5d88dfe45feb4c06a5cfb770"
},
"name": "Floors"
}]
}, {
"_id": {
"$oid": "5d88dfe45feb4c06a5cfb76c"
},
"name": "Building 4",
"subSpace": [{
"_id": {
"$oid": "5d88dfe45feb4c06a5cfb76e"
},
"name": "Basement"
}, {
"_id": {
"$oid": "5d88dfe45feb4c06a5cfb76d"
},
"name": "Floors"
}]
}]
}
For this example, we want to delete the subSpace Floors in Building 2 with this _id : 5d88dfe45feb4c06a5cfb771
My code (in the model) :
exports.removeSubSpaceById = (subSpaceId) => {
Residence.findOneAndUpdate( { "spaces.subSpace._id": '5d88dfe45feb4c06a5cfb771' },
{ $pull:
{ spaces:
{ subSpace:
{ _id: '5d88dfe45feb4c06a5cfb771' }}}}, function(err, result) {
console.log(result);
})
};
Output : console.log : my entire document
But the subSpace/Basement (5d88dfe45feb4c06a5cfb771) is still in my document.
Thanks for your help.
Use positional operator for nested array operations. MongoDb Docs
exports.removeSubSpaceById = (subSpaceId) => {
Residence.findOneAndUpdate({ "spaces._id": '5d88dfe45feb4c06a5cfb76f' },
{
$pull:
{
"spaces.$.subSpace": { _id: "5d88dfe45feb4c06a5cfb771" }
}
}, function (err, result) {
console.log(result);
})
}

Resources