Moogose append to the sub array document - mongodb

I am having a problem with appending data into the sub document.
exports.appendfiles = function(req, res) {
project.findOneAndUpdate(
{
_id: req.body.id
},
{ $push:
{ 'files' : req.files }
},
{
safe: true,
upsert:true
},
function(err, data) {
console.log(err);
return res.send('ok');
}
);
};
This above code does append the data into the sub document, but however you can see the below output it appends a new item with a new index, which is not great. I kept my files model as files : Array any one can help? Much appreciated. the following json is what i have cut out from the document.
{
"buffer": null,
"truncated": false,
"size": 497328,
"extension": "csv",
"path": "uploads/dc45dfeb54c4be89968faa46aabeb114.csv",
"mimetype": "text/csv",
"encoding": "7bit",
"name": "dc45dfeb54c4be89968faa46aabeb114.csv",
"originalname": "obansocial2015-04-20 (7).csv",
"fieldname": "3"
},
{
"0": {
"buffer": null,
"truncated": false,
"size": 8855,
"extension": "html",
"path": "uploads/273bee3485fc564e80d80e92cef32215.html",
"mimetype": "text/html",
"encoding": "7bit",
"name": "273bee3485fc564e80d80e92cef32215.html",
"originalname": "Southern Stars Conference 2014.html",
"fieldname": "0"
},
"1": {
"buffer": null,
"truncated": false,
"size": 383631,
"extension": "mp4",
"path": "uploads/f32da61db7e8df6fccf97b65a788e39d.mp4",
"mimetype": "video/mp4",
"encoding": "7bit",
"name": "f32da61db7e8df6fccf97b65a788e39d.mp4",
"originalname": "video.mp4",
"fieldname": "1"
}
},

You are pushing an array into another, hence it is being nested.
If you want to append each item, use the $each operator:
project.findOneAndUpdate(
{
_id: req.body.id
},
{ $push:
{ 'files' : {$each: req.files} }
},
{
safe: true,
upsert:true
},
function(err, data) {
console.log(err);
return res.send('ok');
}
);

Related

MongoDB $pull failing in different ways intermittently

I tried everything. update, updateMany, findOndAndUpdate, findByIdAndUpdate. I want to delete the object that matches the notification id.
indifferent approach it returns me the object, sometimes returns "mongodb cannot apply $pull to a non-array value"
and sometimes "mongodb matchedCount: 1 but modify 0".
This is one of the examples I tried
const userNotifications = await UserModel.findOneAndUpdate({_id: teacherUpdate.userId}, {
$pull: { notifications: {_id: notificationId} }
})
My DB:
{
"_id": "46df7324d6760d",
"username": "teacher",
"image": null,
"firstname": "Teacher",
"lastname": "Killer",
"skills": null,
"teacher": {
"teacherId": "046e2734d6260dc6"
},
"student": null,
"email": "teacher#gmail.com",
"activeSession": false,
"__v": 1,
"notifications": [
{
"type": "Lesson request",
"content": {
"date": "8.25",
"time": "10",
"status": "Pending",
"studentId": "d03d3c6d0085",
"teacherId": "2732d6286dc6"
},
"_id": "f9659d128c297f85",
"__v": 0
},
{
"type": "Lesson request",
"content": {
"date": "8.26",
"time": "08",
"status": "Pending",
"studentId": "8b4d3d3f8f3485",
"teacherId": "6e273246286d9c6"
},
"_id": "9659d1128c8b",
"__v": 0
}
]
},
Try this.
UserModel.update(
{ '_id': teacherUpdate.userId },
{ $pull: { notifications: { _id: notificationId } } },
false, // Upsert
true, // Multi
);
Edit
this should work
let updatedUser = await UserModel.findOneAndUpdate({ '_id': teacherUpdate.userId }, { $pull: { notifications: { _id: notificationId } } }, {
new: true
});

Querying a map (<String, Object>) in JSON through MongoDB

How to query a map of type Map<String, List> in JSON form, in MongoDB?
Sample JSON:
{
"WIDTH": 810,
"HEIGHT": 465,
"MODULES": {
"23": {
"XNAME": "COMP1",
"PARAMS": {
"_Klockers": {
"TYPE": "text",
"VALUE": "Klocker#3"
},
"SUBSYS": {
"TYPE": "text",
"VALUE": "2"
},
"EP": {
"TYPE": "integer",
"VALUE": "2"
}
}
},
"24": {
"XNAME": "COMP2",
"PARAMS": {
"_Rockers": {
"TYPE": "text",
"VALUE": "Rocker#3"
},
"Driver": {
"TYPE": "binary",
"VALUE": 1
},
"EP": {
"TYPE": "long",
"VALUE": "233"
}
}
},
"25": {
"XNAME": "COMP3",
"PARAMS": {
"_Mockers": {
"TYPE": "text",
"VALUE": "Mocker#3"
},
"SYSMain": {
"TYPE": "text",
"VALUE": "2342"
},
"TLP": {
"TYPE": "double",
"VALUE": "2.3"
}
}
}
}
}
Basically I want to :
List all the "XNAME" field values of all keys in "MODULES".
Expected output : {"COMP1", "COMP2", "COMP3"}
List all the "TYPE" in "PARAMS" object within each key of "MODULES".
Expected output : {"text", "text", "integer", "text", "binary", "long", "text", "text", "double"}
I am new to MongoDB and any help or redirection is appreciated.
You can use this
db.collection.aggregate([
{
$project: {//You require this as your data is dynamic
"modules": {
"$objectToArray": "$MODULES"
}
}
},
{//Destruct the array
"$unwind": "$modules"
},
{
"$project": {//Again, requires the same as keys are dynamic
"types": {
"$objectToArray": "$modules.v.PARAMS"
},
xname: "$modules.v.XNAME"
}
},
{//Destruct the types
$unwind: "$types"
},
{//Get the distinct values
$group: {
"_id": null,
"xname": {
"$addToSet": "$xname"
},
"types": {
"$addToSet": "$types.v.TYPE"
},
}
}
])

How to fix "Can't use $set" error on Mongoose

I'm using mongoose with the given schema:
const StockSchema = new mongoose.Schema(
{
"materialDefinition_id": {
"type": "ObjectId",
"ref": "MaterialDefinition",
"required": true
},
"quantity": {
"type": "Number",
"required": true
},
"committedOrder_id": {
"type": "ObjectId",
"ref": "Order"
},
"committedOrderItemIndex": {
"type": "Number"
},
"detachedOrder_id": {
"type": "ObjectId",
"ref": "Order"
},
"detachedOrderItemIndex": {
"type": "Number"
},
"deletedAt": {
"type": "Number"
},
"createdAt": {
"type": "Number"
},
"updatedAt": {
"type": "Number"
}
});
I'm getting error in the following transaction:
try {
await StockModel.update(
{
$or: [
{ committedOrder_id: purchaseOrder._id },
{ detachedOrder_id: purchaseOrder._id }
]
},
{
$set: {
committedOrder_id: null,
committedOrderItemIndex: null,
detachedOrder_id: null,
detachedOrderItemIndex: null
}
}
);
} catch (err) {
console.log("Error updating stock!");
console.log(err);
}
Error:
Error updating stock!
Error: Can't use $set
at ObjectId.SchemaType.castForQuery (/Users/workspace/dev/app/dev/node_modules/mongoose/lib/schematype.js:1368:13)
at ObjectId.SchemaType.castForQueryWrapper (/Users/workspace/dev/app/dev/node_modules/mongoose/lib/schematype.js:1347:17)
at cast (/Users/workspace/dev/app/dev/node_modules/mongoose/lib/cast.js:288:39)
at model.Query.Query.cast (/Users/workspace/dev/app/dev/node_modules/mongoose/lib/query.js:4644:12)
at model.Query.Query._castConditions (/Users/workspace/dev/app/dev/node_modules/mongoose/lib/query.js:1842:10)
at model.Query.<anonymous> (/Users/workspace/dev/app/dev/node_modules/mongoose/lib/query.js:2097:8)
at model.Query._wrappedThunk [as _findOne] (/Users/workspace/dev/app/dev/node_modules/mongoose/lib/helpers/query/wrapThunk.js:16:8)
at /Users/workspace/dev/app/dev/node_modules/kareem/index.js:369:33
at processTicksAndRejections (internal/process/task_queues.js:75:11)
Any ideas on how to fix it?
From the documentation, update is executed on a document object, see document.prototype.update
You can try this instead:
await StockModel.findOneAndUpdate({
$where: { $or: [{ committedOrder_id: purchaseOrder._id }, { detachedOrder_id: purchaseOrder._id }] },
$set: {
committedOrder_id: null,
committedOrderItemIndex: null,
detachedOrder_id: null,
detachedOrderItemIndex: null
}
});

Mongo Aggregation using $Max

I have a collection that stores history, i.e. a new document is created every time a change is made to the data, I need to extract fields based on the max value of a date field, however my query keeps returning either all of the dates or requires me to push the fields into an array which make the data hard to analyze for an end-user.
Expected output as CSV:
MAX(DATE), docID, url, type
1579719200216, 12371, www.foodnetwork.com, food
1579719200216, 12371, www.cnn.com, news,
1579719200216, 12371, www.wikipedia.com, info
Sample Doc:
{
"document": {
"revenueGroup": "fn",
"metaDescription": "",
"metaData": {
"audit": {
"lastModified": 1312414124,
"clientId": ""
},
"entities": [],
"docId": 1313943,
"url": ""
},
"rootUrl": "",
"taggedImages": {
"totalSize": 1,
"list": [
{
"image": {
"objectId": "woman-reaching-for-basket",
"caption": "",
"url": "",
"height": 3840,
"width": 5760,
"owner": "Facebook",
"alt": "Woman reaching for basket"
},
"tags": {
"totalSize": 4,
"list": []
}
}
]
},
"title": "The 8 Best Food Items of 2020",
"socialTitle": "The 8 Best Food Items of 2020",
"primaryImage": {
"objectId": "woman-reaching-for-basket.jpg",
"caption": "",
"url": "",
"height": 3840,
"width": 5760,
"owner": "Hero Images / Getty Images",
"alt": "Woman reaching for basket in laundry room"
},
"subheading": "Reduce your footprint with these top-performing diets",
"citations": {
"list": []
},
"docId": 1313943,
"revisionId": "1313943_1579719200216",
"templateType": "LIST",
"documentState": {
"activeDate": 579719200166,
"state": "ACTIVE"
}
},
"url": "",
"items": {
"totalSize": "",
"list": [
{
"type": "recipe",
"data": {
"comInfo": {
"list": [
{
"type": "food",
"id": "https://www.foodnetwork.com"
}
]
},
"type": ""
},
"id": 4,
"uuid": "1313ida-qdad3-42c3-b41d-223q2eq2j"
},
{
"type": "recipe",
"data": {
"comInfo": {
"list": [
{
"type": "news",
"id": "https://www.cnn.com"
},
{
"type": "info",
"id": "https://www.wikipedia.com"
}
]
},
"type": "PRODUCT"
},
"id": 11,
"uuid": "318231jc-da12-4475-8994-283u130d32"
}
]
},
"vertical": "food"
}
Below query:
db.collection.aggregate([
{
$match: {
vertical: "food",
"document.documentState.state": "ACTIVE",
"document.templateType": "LIST"
}
},
{
$unwind: "$document.items"
},
{
$unwind: "$document.items.list"
},
{
$unwind: "$document.items.list.contents"
},
{
$unwind: "$document.items.list.contents.list"
},
{
$match: {
"document.items.list.contents.list.type": "recipe",
"document.revenueGroup": "fn"
}
},
{
$sort: {
"document.revisionId": -1
}
},
{
$group: {
_id: {
_id: {
docId: "$document.docId",
date: {$max: "$document.revisionId"}
},
url: "$document.items.list.contents.list.data.comInfo.list.id",
type: "$document.items.list.contents.list.data.comInfo.list.type"
}
}
},
{
$project: {
_id: 1
}
},
{
$sort: {
"document.items.list.contents.list.id": 1, "document.revisionId": -1
}
}
], {
allowDiskUse: true
})
First of all, you need to go through the documentation of the $group aggregation here.
you should be doing this instead:
{
$group: {
"_id": "$document.docId"
"date": {
$max: "$document.revisionId"
},
"url": {
$first: "$document.items.list.contents.list.data.comInfo.list.id"
},
"type": {
$first:"$document.items.list.contents.list.data.comInfo.list.type"
}
}
}
This will give you the required output.

How to get ids of parents based on deeply nested sub document?

This question is in continuation with older question: Parent.save() not working when sub document / deeply nested document is modified
Say I have a document as below :
{
"apiCallCount": 1,
"_id": "5e0da052b4b3fe5188602e11",
"email": "abc#def.net",
"password": "123123",
"userName": "username",
"companyName": "companyName",
"apiKey": "apiKey",
"solutionType": "solutionType",
"parentCompany": "parentCompany",
"buildings": [
{
"gateways": [
{
"devices": [
{
"_id": "5e0da052b4b3fe5188602e15",
"serialNumber": "serialNumber 1",
"area": "area",
"connectionStatus": 0,
"gatewayKey": "gatewayKey",
"applicationNumber": 11,
"firmwareVersion": "firmwareVersion",
"needsAttention": true,
"verificationCode": "123456",
"patientRiskStatus": "patientRiskStatus",
"patientFirstName": "UPDATED!!!",
"patientLastName": "patientLastName",
"createdAt": "2020-01-02T07:48:34.287Z",
"updatedAt": "2020-01-02T07:48:34.287Z"
},
{
"_id": "5e0da052b4b3fe5188602e14",
"serialNumber": "serialNumber 2",
"area": "area",
"connectionStatus": 0,
"gatewayKey": "gatewayKey",
"applicationNumber": 22,
"firmwareVersion": "firmwareVersion",
"needsAttention": true,
"verificationCode": "987654",
"patientRiskStatus": "patientRiskStatus",
"patientFirstName": "patientFirstName",
"patientLastName": "patientLastName",
"createdAt": "2020-01-02T07:48:34.288Z",
"updatedAt": "2020-01-02T07:48:34.288Z"
}
],
"_id": "5e0da052b4b3fe5188602e13",
"gatewayName": "gatewayName 1",
"gatewayKey": "gatewayKey",
"suite": "suite",
"createdAt": "2020-01-02T07:48:34.288Z",
"updatedAt": "2020-01-02T07:48:34.288Z"
}
],
"_id": "5e0da052b4b3fe5188602e12",
"buildingName": "buildingName 1",
"address": "address",
"suite": "suite",
"floor": "floor",
"timeZone": "String",
"createdAt": "2020-01-02T07:48:34.288Z",
"updatedAt": "2020-01-02T07:48:34.288Z"
}
],
"createdAt": "2020-01-02T07:48:34.289Z",
"updatedAt": "2020-01-02T09:10:25.200Z",
"__v": 0
}
I am able to dig through document and able to get device sub document with "verificationCode": "123456"
Now I want to get gatewayID(one level up) and buildingID(two level up) for this device.
Currently I have a call like this :
I am trying to update parent doc based on deeply nested sub-document.
I get sub document by accountId and verification code like below.
and then need to update the parent.
In my sample below I Put hard-coded ids which i need to get run time.
if (newlySavedUser) {
try {
let result = await Account.findByIdAndUpdate(
accountId,
{
$set: {
"buildings.$[building].gateways.$[gateway].devices.$[device].patientFirstName": "userName",
"buildings.$[building].gateways.$[gateway].devices.$[device].patientLastName": "userName1"
}
},
{
arrayFilters: [
{ "building._id": ObjectId("5d254bb179584ebcbb68b712") }, /// <---- I want to get this buildingId
{ "gateway._id": ObjectId("5d254b64ba574040d9632ada") }, /// <---- I want to get this gatewayId
{ "device.verificationCode": "4144" } /// <-- based on this verificationCode
],
new: true
}
);
if (!result) return res.status(404);
console.log(result)
//res.send(result);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
}
Trying "tom slabbaert" solution for aggregate.
Account.aggregate([
{
$unwind: "$buildings"
},
{
$unwind: "$gateways"
},
{
$match: {
"buildings.gateways.devices.verificationCode": "4144"
}
},
{
$project: {
buildingID: "$buildings._id",
gatewayID: "$gateways._id",
}
}
]).exec((err, result)=>{
console.log("result", result)
if(err) throw err;
});
Your help is appreciated.
You can use this 3 level $unwind aggregation, and then match the document you want:
db.collection.aggregate([
{
$unwind: "$buildings"
},
{
$unwind: "$buildings.gateways"
},
{
$unwind: "$buildings.gateways.devices"
},
{
$match: {
"buildings._id": "5e0da052b4b3fe5188602e12",
"buildings.gateways._id": "5e0da052b4b3fe5188602e13",
"buildings.gateways.devices.verificationCode": "123456"
}
}
])
This will give you the following result:
[
{
"__v": 0,
"_id": "5e0da052b4b3fe5188602e11",
"apiCallCount": 1,
"apiKey": "apiKey",
"buildings": {
"_id": "5e0da052b4b3fe5188602e12",
"address": "address",
"buildingName": "buildingName 1",
"createdAt": "2020-01-02T07:48:34.288Z",
"floor": "floor",
"gateways": {
"_id": "5e0da052b4b3fe5188602e13",
"createdAt": "2020-01-02T07:48:34.288Z",
"devices": {
"_id": "5e0da052b4b3fe5188602e15",
"applicationNumber": 11,
"area": "area",
"connectionStatus": 0,
"createdAt": "2020-01-02T07:48:34.287Z",
"firmwareVersion": "firmwareVersion",
"gatewayKey": "gatewayKey",
"needsAttention": true,
"patientFirstName": "UPDATED!!!",
"patientLastName": "patientLastName",
"patientRiskStatus": "patientRiskStatus",
"serialNumber": "serialNumber 1",
"updatedAt": "2020-01-02T07:48:34.287Z",
"verificationCode": "123456"
},
"gatewayKey": "gatewayKey",
"gatewayName": "gatewayName 1",
"suite": "suite",
"updatedAt": "2020-01-02T07:48:34.288Z"
},
"suite": "suite",
"timeZone": "String",
"updatedAt": "2020-01-02T07:48:34.288Z"
},
"companyName": "companyName",
"createdAt": "2020-01-02T07:48:34.289Z",
"email": "abc#def.net",
"parentCompany": "parentCompany",
"password": "123123",
"solutionType": "solutionType",
"updatedAt": "2020-01-02T09:10:25.200Z",
"userName": "username"
}
]
Mongoplayground
A simple aggregation would suffice:
db.collection.aggregate([
{
$unwind: "$buildings"
},
{
$unwind: "$buildings.gateways"
},
{
$match: {
"buildings.
gateways.devices.verificationCode": "123456"
}
},
{
$project: {
buildingID: "$buildings._id",
gatewayID: "$gateways._id",
}
}
])
If its possible to get duplicates you can $group on buildingID to avoid that.