Mongoose findOneAndUpdate inside nested Arrays - javascript

I need to update the type, key and values in the 'fields' array if the componentId and the wfInstanceId id matches.
This is the document that i need to do so.
{
"_id": {
"$oid": "586b6d756937c22f207dd5af"
},
"wfInstanceId": "0111",
"workflowId": "ash3",
"folderURL": "ash3",
"teamName": "teamName",
"dataStream": [
{
"componentInstanceId": "componentInstanceId1",
"componentId": "componentId1",
"_id": {
"$oid": "586b6d756937c22f207dd5b0"
},
"fields": [
{
"type": "String",
"value": "value1",
"key": "key",
"_id": {
"$oid": "586b6d756937c22f207dd5b1"
}
},
{
"type": "String",
"value": "value2",
"key": "key1",
"_id": {
"$oid": "586b6d756937c22f207dd5b1"
}
}
]
},
{
"componentInstanceId": "componentInstanceId2",
"componentId": "componentId22",
"_id": {
"$oid": "586b6d756937c22f207dd5b0"
},
"fields": [
{
"type": "String",
"value": "value1",
"key": "key",
"_id": {
"$oid": "586b6d756937c22f207dd5b1"
}
},
{
"type": "String",
"value": "value2",
"key": "key2",
"_id": {
"$oid": "586b6d756937c22f207dd5b1"
}
}
]
}
],
"id": "38f356f0-d196-11e6-b0b9-3956ed7f36f0",
"__v": 0
}
I tried this like this, Also i tried $set over $push which is also doesn't work.
Model.findOneAndUpdate( {'wfInstanceId': '0111', 'dataStream.componentId': 'componentId1'}, {$push : { 'dataStream.fields.$.key' : 'sdsdds' }}, { upsert: true }, function (err, data) {
if (err) {
reject(err);
console.log('error occured' + err);
}
console.info("succesfully saved");
resolve (data);
});
As they describe in this DOC it shouldbe working for update method that is also didn't work for me. Any help will be really appreciated to overcome this issue i'm facing.

As you are using $ operator it updates only the first matched array element in each document.
As of now it is not possible in mongoose to directly update all array elements.
You can do this in your case:
db.collection.find({'wfInstanceId': '0111', 'dataStream.componentId': 'componentId1'})
.forEach(function (doc) {
doc.datastream.forEach(function (datastream) {
if (dataStream.componentId === componentId1) {
dataStream.fields.forEach(function(fields){
// you can also write condition for matching condition in field
dataStream.fields.key="";
dataStream.fields.value="";
dataStream.fields.type="";
}
}
});
db.collection.save(doc);
});
It is normal javascript code. I think it's clearer for mongo newbies.

Related

How to remove an entire array in mongodb- node

This is what I tred
removeFromCart:(userId)=>{
return new Promise(async (resolve,reject)=>{
db.get().collection(collection.CART_COLLECTION).updateOne({_id:userId},
{
$pull:{
products
}
}
)
resolve(response)
})
}
This is the structure of my database, after placing the orde, I have to remove the cart array , Then what I have to do?
{
"_id": {
"$oid": "633abca2f9a57f6470904812"
},
"user": {
"$oid": "633ab3c11e6e97b6332f56a1"
},
"products": [
{
"item": {
"$oid": "63314b0c07e279ee33c55caf"
},
"quantity": 2
}
]
}
You can use the $unset operator:
db.collection.update({
"_id": "633abca2f9a57f6470904812"
},
{
"$unset": {
"products": ""
}
})
Here's an example on mongoplayground: https://mongoplayground.net/p/X9jEnmKjCGK

How to return single object (mongoose/mongoDB)

I have this data stored in database.
{
"_id": "62fa5aa25778ec97bc6ee231",
"user": "62f0eb5ebebd0f236abcaf9d",
"name": "Marketing Plan",
"columns": [
{
"name": "todo",
"_id": "62fa5aa25778ec97bc6ee233",
"tasks": [
{
"title": "Task Four testing 2",
"description": "This is task four",
"subtasks": [
{
"name": "wash dshes test",
"completed": false,
"_id": "62ff74bfe80b11ade2d34456"
},
{
"name": "do homework",
"completed": false,
"_id": "62ff74bfe80b11ade2d34457"
}
],
"_id": "62ff74bfe80b11ade2d34455"
}
]
},
{
"name": "doing",
"_id": "62fa5aa25778ec97bc6ee234",
"tasks": []
},
{
"name": "done",
"_id": "62fa5aa25778ec97bc6ee235",
"tasks": []
}
],
"__v":0
}
I want to be able to return a single object with the id equal to the req.params.id, in this case that would be 62ff74bfe80b11ade2d34455.
{
"title": "Task Four testing 2",
"description": "This is task four",
"subtasks": [
{
"name": "wash dshes test",
"completed": false,
"_id": "62ff74bfe80b11ade2d34456"
},
{
"name": "do homework",
"completed": false,
"_id": "62ff74bfe80b11ade2d34457"
}
],
"_id": "62ff74bfe80b11ade2d34455"
}
I researched stackoverflow and came across this potential solution: Mongoose retrieve one document from nested array which implemented the aggregate framework. But when I test this in postman, the request isn't made.
const getTask = asyncHandler(async (req, res) => {
const task = await Board.aggregate([
{
$match: {
"columns.tasks._id": req.params.id,
},
},
{
$project: {
columns: {
$first: {
$filter: {
input: "$columns.tasks",
cond: {
$eq: ["$$this._id", req.params.id],
},
},
},
},
},
},
{
$replaceRoot: {
newRoot: "$columns",
},
},
]);
});
Having an array inside an array complicates the query a bit, but here's one way to retrieve the data you want.
db.Board.aggregate([
{
$match: {
"columns.tasks._id": req.params.id
}
},
{"$unwind": "$columns"},
{
$match: {
"columns.tasks._id": req.params.id
}
},
{
"$project": {
"task": {
"$first": {
"$filter": {
"input": "$columns.tasks",
"cond": {"$eq": ["$$this._id", req.params.id]}
}
}
}
}
},
{"$replaceWith": "$task"}
])
Try it on mongoplayground.net. [The mongoplayground.net example uses "62ff74bfe80b11ade2d34455" rather than req.params.id.]

How to remove deeply nested object (Node.js, mongoose)

I'm making a kanban task management app and I'm trying to remove a task with the _id: req.params.id which has the value of 62fa5ae05778ec97bc6ee23a. I tried the following:
const task = await Board.findOneAndUpdate(
{
"columns.tasks._id": req.params.id,
},
{ $pull: { "columns.$.tasks.$._id": req.params.id } },
{ new: true }
);
But I get the error Too many positional (i.e. '$') elements found in path'columns.$.tasks.$._id'
I searched for a while and came across arrayFilters from the docs but I'm struggling a lot to understand how to implement it for this particular need.
{
"_id": "62fa5aa25778ec97bc6ee231",
"user": "62f0eb5ebebd0f236abcaf9d",
"name": "Marketing Plan",
"columns": [
{
"name": "todo",
"_id": "62fa5aa25778ec97bc6ee233",
"tasks": [
{
"title": "Task Four",
"description": "This is task four",
"subtasks": [
{
"name": "wash dshes",
"completed": false,
"_id": "62fa5ae05778ec97bc6ee23b"
},
{
"name": "do homework",
"completed": false,
"_id": "62fa5ae05778ec97bc6ee23c"
}
],
"_id": "62fa5ae05778ec97bc6ee23a"
}
]
},
{
"name": "doing",
"_id": "62fa5aa25778ec97bc6ee234",
"tasks": []
},
{
"name": "done",
"_id": "62fa5aa25778ec97bc6ee235",
"tasks": []
}
],
"__v": 0
}
You need to use $[] positional operator in order to pull from the nested array. Try running this query:
db.Board.updateOne({
"_id" : "62fa5aa25778ec97bc6ee231",
}, {
$pull: { 'columns.$[].tasks': { '_id': '62fa5ae05778ec97bc6ee23a' } }
});

Javascript how to add and remove JSON element which is on 2 level deep

I googled and monkey around a lot on how to remove elements from my JSON object.
Here is my simplified JSON to illustrate my requirement.
bundle:
{
"resourceType": "Bundle",
"meta": {
"lastUpdated": "2017-10-06T04:42:22.411Z"
},
"type": "searchset",
"total": "0",
"entry": [
{
"_id": "59d5739e668e9e3fd29aeb0d",
"resource": {
"id": "59d5739e668e9e3fd29aeb0d"
},
"__v": 0
},
{
"_id": "59d6a3fae4b45d50c5ffd4f7",
"resource": {
"id": "59d6a3fae4b45d50c5ffd4f7"
},
"__v": 0
},
{
"_id": "59d6a831e4b45d50c5ffd4fa",
"resource": {
"id": "59d6a831e4b45d50c5ffd4fa"
},
"__v": 0
}
]
}
How to remove ALL entry._id and entry.__v?
I tried these, but not working.
delete bundle.meta; <-- meta is on level 1, it works.
delete bundle.entry._id; <-- not working. The _id under entry
delete bundle.__v; <-- not working. The __v is also under entry
How can I add a new element under each entry such as "fullname" as below
My desired result - remove '_id' and '__v', then add 'fullname' to the entry[]:
{
"resourceType": "Bundle",
"meta": {
"lastUpdated": "2017-10-06T04:42:22.411Z"
},
"type": "searchset",
"total": "0",
"entry": [
{
"fullname": "Apple",
"resource": {
"id": "59d5739e668e9e3fd29aeb0d",
}
},
{
"fullname": "Orange",
"resource": {
"id": "59d6a3fae4b45d50c5ffd4f7"
},
}, ......
I have tried many ways and googled a lot. Thanks for the help!
Thanks Vivek for the answer. It works.
for (var i = 0; i < bundle.entry.length; i++) {
delete bundle.entry[i]._id;
delete bundle.entry[i].__v;
bundle.entry[i].fullname = "Test";
}
Please try the code below. Tested it can help you.
var object1 = {
"resourceType": "Bundle",
"meta": {
"lastUpdated": "2017-10-06T04:42:22.411Z"
},
"type": "searchset",
"total": "0",
"entry": [
{
"_id": "59d5739e668e9e3fd29aeb0d",
"resource": {
"id": "59d5739e668e9e3fd29aeb0d",
},
"__v": 0
},
{
"_id": "59d6a3fae4b45d50c5ffd4f7",
"resource": {
"id": "59d6a3fae4b45d50c5ffd4f7"
},
"__v": 0
},
{
"_id": "59d6a831e4b45d50c5ffd4fa",
"resource": {
"id": "59d6a831e4b45d50c5ffd4fa"
},
"__v": 0
}
]
};
var array1=['Apple','Orange','Gava']
function a(){
for(var i=0;i<object1.entry.length;i++){
delete object1.entry[i]._id;
delete object1.entry[i].__v;
object1.entry[i].fullname = array1[i];
}
console.log(object);
}
bundle.entry is an Array. :)
delete _id of first element:
delete bundle.entry[0]._id
delete _id and __v of all element:
bundle.entry = bundle.entry.map(function(entry){
var newEntry = {
resource: entry.resource,
fullname: "fullname here"
};
return newEntry;
});
const fullNameList = ['Apple', 'Orange', 'Guava']
delete bundle.meta
bundle.entry = bundle.entry.map((item, index) => {
const {__v, _id, ...restObj } = item;
restObj.fullname = fullNameList[index]
return restObj;
})
mind that the fullNameList's length and the bundle.entry's length must be the same
You can use the map function and remove the keys from the object, and add keys in the same function, as shown below:
var data = {
"resourceType": "Bundle",
"meta": {
"lastUpdated": "2017-10-06T04:42:22.411Z"
},
"type": "searchset",
"total": "0",
"entry": [
{
"_id": "59d5739e668e9e3fd29aeb0d",
"resource": {
"id": "59d5739e668e9e3fd29aeb0d"
},
"__v": 0
},
{
"_id": "59d6a3fae4b45d50c5ffd4f7",
"resource": {
"id": "59d6a3fae4b45d50c5ffd4f7"
},
"__v": 0
},
{
"_id": "59d6a831e4b45d50c5ffd4fa",
"resource": {
"id": "59d6a831e4b45d50c5ffd4fa"
},
"__v": 0
}
]
}
var names = ['A','B','C'];
var counter = 0;
data.entry = data.entry.map(function(obj){
delete obj['_id']
delete obj['__v']
obj.fullname = names[counter]
counter++ ;
return obj
});
console.log(data);

Trouble creating query in MongoDB with subquery

I have a dataset that looks something like this:
{
"id": "02741544",
"items": [{
"item": "A"
}]
}, {
"id": "02472691",
"items": [{
"item": "A"
}, {
"item": "B"
}, {
"item": "C"
}]
}, {
"id": "01316523",
"items": [{
"item": "A"
}, {
"item": "B"
}]
}, {
"id": "01316526",
"items": [{
"item": "A"
}, {
"item": "B"
}]
}, {
"id": "01316529",
"items": [{
"item": "A"
}, {
"item": "D"
}]
},
I'm trying to craft a query that will give me an output that looks like this:
{
"item": "A",
"ids": [{
"id": "02741544"
}, {
"id": "02472691"
}, {
"id": "01316523"
}, {
"id": "01316526"
}, {
"id": "01316529"
}]
}, {
"item": "B",
"ids": [{
"id": "02472691"
}, {
"id": "01316523"
}, {
"id": "01316526"
}]
}, {
"item": "C",
"ids": [{
"id": "02472691"
}]
}, {
"item": "D",
"ids": [{
"id": "02472691"
}]
},
Basically, I'm trying to get the distinct items from the item array in the object, and then returning an array of ids for each obj that has that item in it's item array.
Better use the aggregation framework in which you need to run an operation that consists of the following pipeline steps (in the given order):
$unwind - This initial step will flatten the items array i.e. it produces a copy of each document per array entry. This is necessary for processing the documents further down the pipeline as "denormalised" documents which you can aggregate as groups.
$group - This will group the flattened documents by the item subdocument key and create the ids list by using the $push accumulator operator.
-- UPDATE --
As #AminJ pointed out in the comments, if items can have duplicate item values and you don't want duplicate ids in the result you can use $addToSet instead of $push
The following example demonstrates this:
db.collection.aggregate([
{ "$unwind": "$items" },
{
"$group": {
"_id": "$items.item",
"ids": {
"$push": { "id": "$id" } /* or use
"$addToSet": { "id": "$id" } if you don't want duplicate ids */
}
}
}
])
Sample Output
{
"_id" : "A",
"ids" : [
{ "id" : "02741544" },
{ "id" : "02472691" },
{ "id" : "01316523" },
{ "id" : "01316526" },
{ "id" : "01316529" }
]
}
/* 2 */
{
"_id" : "B",
"ids" : [
{ "id" : "02472691" },
{ "id" : "01316523" },
{ "id" : "01316526" }
]
}
/* 3 */
{
"_id" : "C",
"ids" : [
{ "id" : "02472691" }
]
}
/* 4 */
{
"_id" : "D",
"ids" : [
{ "id" : "01316529" }
]
}
The result from an aggregate() function is a cursor to the documents produced by the final stage of the aggregation pipeline operation. So if you want the results in an array you can use the cursor's toArray() method which returns an array that contains all the documents from it.
For example:
var pipeline = [
{ "$unwind": "$items" },
{
"$group": {
"_id": "$items.item",
"ids": {
"$push": { "id": "$id" } /* or use
"$addToSet": { "id": "$id" } if you don't want duplicate ids */
}
}
}
],
results = db.collection.aggregate(pipeline).toArray();
printjson(results);
Here's a solution using an aggregation pipeline:
db.col.aggregate([
{
$unwind: "$items"
},
{
$project: {
id: 1,
item: "$items.item"
}
},
{
$group: {
_id: "$item",
ids: {
$push: "$id"
}
}
}
])

Categories

Resources