Mongoose findByIdAndUpdate destroys missing fields - node.js

Developing a node.js api. I have mongo data that shows venues. It contains an address, for example....
"address": {
"street": "123 main st",
"city": "seomwhere",
"state": "FL",
"zip": "33222"
},
:
When I POST to the update endpoint and send the body like this it does update the address, but it REMOVES all the other fields that were there....
"address": {
"zip": "33222"
}
So, in the db, street, city, state are missing. Here's a piece of my code...
venue = await Venue.findByIdAndUpdate(req.params.id, req.body, {
new: true,
runValidators: true
});
Any ideas why?

Because address contains only single key in the body ("address": { "zip": "33222" }) it replaces the other values inside the address object with the blank ones in the database.
So to overcome the issue you have to use . notation with the address object. Like
venue = await Venue.findByIdAndUpdate(
req.params.id,
{ $set: { "address.zip": "33222" },
{ new: true, runValidators: true }
)
But as you cannot identify which keys inside the address object will be updated and also for the root level
const object = req.body
for (var key in req.body.address) {
object[`address.${key}`] = object.address[key]
}
delete object.address
venue = await Venue.findByIdAndUpdate(
req.params.id,
{ $set: object },
{ new: true, runValidators: true }
)

You are overwriting existing address fields when not specified in the request body.
You need to only update the fields in the request body:
const fields = {};
Object.keys(req.body.address).forEach(key => {
fields[`address.${key}`] = req.body.address[key];
});
const venue = await Venue.findByIdAndUpdate(req.params.id, fields, {
new: true,
runValidators: true
});
Let's say we have this venue document:
{
"_id": "5e047d4e10be4f0da4c61703",
"name": "Venue 1",
"address": {
"street": "123 main st",
"city": "seomwhere",
"state": "FL",
"zip": "33222"
},
"__v": 0
}
When you use a requset body like this:
{
"address": {
"zip": "33333"
}
}
The response will be:
{
"address": {
"street": "123 main st",
"city": "seomwhere",
"state": "FL",
"zip": "33333"
},
"_id": "5e047d4e10be4f0da4c61703",
"name": "Venue 1",
"__v": 0
}
And when we want to update like this:
{
"address": {
"state": "NY",
"zip": "44444"
}
}
The response will be:
{
"address": {
"street": "123 main st",
"city": "seomwhere",
"state": "NY",
"zip": "44444"
},
"_id": "5e047d4e10be4f0da4c61703",
"name": "Venue 1",
"__v": 0
}

Related

Using cond in mongo aggregate match expression

So i'm trying to query products based on the user specified filter. if the user specifies the size, for example XS, then i fetch all the products with 'XS' sizes, otherwise if the size isn't specified then i will fetch all the products without checking for their size. How can i do it with the mongo aggregation ?
here's my code.
1st: here's 2 products documents from my collection
[
{
"_id": "6124d16b1396d20aea617d75",
"username": "josh988",
"userpic": "josh.jpg",
"adresse": "545 Strawberry Lane Mokena, IL 60448",
"city": "Paris",
"country": "France",
"product": {
"_id": "62c064103a7b5160e957764c",
"images": [
"img1734116er1111111.jpeg"
],
"name": "White shirt",
"category": "Kids",
"subCategory": "Tshirts",
"size": "XS",
"price": 150
}
},
{
"_id": "6124d16b1396d20aea617d75",
"username": "laurel22",
"userpic": "laurel.jpg",
"adresse": "545 Strawberry Lane Mokena, IL 60448",
"city": "Lyon",
"country": "France",
"product": {
"_id": "62c064103a7b5160e957764c",
"images": [
"img1711656775696063.jpeg"
],
"name": "Black shirt",
"category": "Kids",
"subCategory": "Tshirts",
"size": "M",
"price": 250
}
},
]
to make the matter simple , my body api body request looks like this
{
"selectedCategory": "Kids",
"selectedSubCategory": "Tshirts",
// for e.g selectedSize could be XS
"selectedSize": null
}
So what i'd like to do is, if the user select a size then fetch the products with it otherwise fetch everything.
I tried this with mongo but didn't work
const searchResults = await user.aggregate([
{
$match: {
"product.category": `${req.body.selectedCategory}`,
"product.subCategory": `${req.body.selectedSubCategory}`,
$expr: {
$cond: {
// what should i do here ? or is there another way of achieving this
if: { $ne: [`${req.body.selectedSize}`, null] },
then: { product.size : req.body.selectedSize },
// and what should write here to fetch everything
else: { product.size: "" },
},
},
},
},
]);
You can generate the aggregation pipeline conditionally in nodejs itself. Like this:
let matchStage = {
$match: {
"product.category": `${req.body.selectedCategory}`,
"product.subCategory": `${req.body.selectedSubCategory}`,
}
};
if(req.body.selectedSize) {
matchStage = {
$match: {
...matchStage["$match"],
"product.size": request.body.selectedSize
}
};
}
const searchResults = await user.aggregate([matchStage]);

Search Filter MongoDB Nestjs

When Implementing a search filter on a document withh populated documents and array of object, How can I enable the search filter to checkout the populated document or array for the searched word?
What I have only checks through the surface fields
async findAllFeedbacks(user: any, payload: FilterDto) {
if (payload.fields) {
const query = await this.distributionChannelsDocumentModel
.find({
[payload.fields]: {
$regex: `${payload.searchFilter}`,
$options: 'i'
},
})
.clone()
.where({ business: user.business })
.populate({
path: 'feedbackId',
// match: { 'feedbackId[payload.fields]': { $regex: `${payload.searchFilter}` } }
})
return { query, total: query.length }
} else {
const query = await this.distributionChannelsDocumentModel
.find()
.clone()
.where({ business: user.business })
.populate('feedbackId')
return { query, total: query.length }
}
}
Here is a response sample:
{
"query": [
{
"_id": "62a8645641b312b3a04a7233",
"feedbackId": {
"_id": "62a8642b41b312b3a04a722a",
"title": "Monthly Feedback test",
"questions": [
{
"title": "Rate Our Services test",
"subtitle": "Customer retention is our priority",
"type": "SMILEY"
}
],
"thankYouMessage": "Thanks for dropping a feedback",
"allowComment": true,
"allowEmail": true,
"allowFullName": false,
"allowAttachment": false,
"status": "Active",
"business": "62a83f5ab4b33de7307e9cb8",
"user": "62a83f59b4b33de7307e9cb6",
"createdAt": "2022-06-14T10:34:19.709Z",
"updatedAt": "2022-06-14T10:34:19.709Z",
"__v": 0
},
"user": "62a83f59b4b33de7307e9cb6",
"distributionChannelType": [
"email"
],
"meta": [
{
"sender": "string",
"subject": "string",
"positioning": "string",
"timing": "string",
"brandColor": "string"
}
],
"createdAt": "2022-06-14T10:35:02.092Z",
"updatedAt": "2022-06-14T10:35:02.092Z",
"__v": 0
},
{
"_id": "62b1c87510fae7f3a54b13b2",
"feedbackId": {
"_id": "62ac55d0b28630c3a8c9b940",
"title": "Yearly Feedback",
"questions": [
{
"title": "Rate Our Services",
"subtitle": "We love to hear from you! How is your Jumia food experience",
"type": "SMILEY"
}
],
"thankYouMessage": "Thanks for dropping a feedback",
"allowComment": true,
"allowEmail": true,
"allowFullName": false,
"allowAttachment": false,
"status": "Active",
"business": "62ac434542452ccb513f2241",
"user": "62ac434542452ccb513f223f",
"createdAt": "2022-06-17T10:22:08.863Z",
"updatedAt": "2022-06-17T10:22:08.863Z",
"__v": 0
},
"user": "62ac434542452ccb513f223f",
"distributionChannelType": [
"sms"
],
"meta": [
{
"position": "bottom",
"timing": "",
"brandColor": "#444"
}
],
"createdAt": "2022-06-21T13:32:37.895Z",
"updatedAt": "2022-06-21T13:32:37.895Z",
"__v": 0
}
],
"total": 2
}
In the above response, feedbackId is the populated document and questions is the array I would also want to search through when using the search filter

How do i get data from user table in mongos node js by relation id

I have a one order table
Mongo db version is 3.0.15
Order table have this columns
{
"status": "new",
"_id": "5fd320a8b3d5133c8bf00c4a",
"categoryId": "5fb2e20f5ea2aa2d15cca1ef",
"specialization": "Tesst",
"requestedBy": "5fd232ecd7ba652c3a85f818",
"vendorId": "5fa908773410d8591aa9f550",
"updated_at": 1607671976182,
"created_at": 1607671976182,
},
requested-by belongs to second table users
I want to fetch user details from user table username user-address
in user table i have this columns
{
"_id": "5fd232ecd7ba652c3a85f818",
"phone": "1234567890",
"otp" : "123456",
"progress_pictures": [],
"createdAt": "2020-12-10T14:38:36.045Z",
"updatedAt": "2020-12-10T14:39:35.127Z",
}
How can I get data anyone know ?
Order.find(
{
status : status
},
{
}
).exec();
This is my order invite query this is working find get only order table data
i found solution using populate
Order.find(
{
status : status
},
{
}
).populate([
{
path: 'requestedBy', // in order table user ref id
select : 'name phone user_address'
},{
path : 'vendorId', // in order table vendor ref id
select : 'vendorName vendorMobile vendorAddress vendorBusinessName'
}]).exec();
----------------------- RESPONSE -----------------------
{
"status": "new",
"_id": "5fd320a8b3d5133c8bf00c4a",
"categoryId": "5fb2e20f5ea2aa2d15cca1ef",
"specialization": "Tesst",
"requestedBy": {
"user_address": [],
"name" : "nikhil",
"_id": "5fd232ecd7ba652c3a85f818",
"phone": "1234567890"
},
"vendorId": {
"_id": "5fa908773410d8591aa9f550",
"vendorMobile": "1234567890",
"vendorAddress" : "demo address",
"vendorBusinessName" : "testing hub",
"vendorName": "KL"
},
"updated_at": 1607671976182,
"created_at": 1607671976182,
},

Mongoose: Update of array inside another nested array element not working as expected

Here is my mongo schema:
{
"_id": ObjectId("5f8af2fc5f23667adf3bbaf2"),
"score": 2.5,
"questions": [{
"_id": ObjectId("5f8af30d5f23667adf3bbaf5"),
"desc": "some text",
},
{
"_id": ObjectId("5f8af3115f23667adf3bbaf8"),
"desc": "some text",
"options": [{
"_id": ObjectId("5f8af3115f23667adf3bbaf9"),
"name": "some name",
"desc": "description 1"
},
{
"_id": ObjectId("5f8af3115f23667adf3bbafa"),
"name": "some name",
"desc": "description 2"
}
]
}
]
}
I've to update the name and desc of the option having id as 5f8af3115f23667adf3bbaf9 which is in the one of the array elements of the question attribute having id as 5f8af30d5f23667adf3bbaf5 which is again part of the data having id as 5f8af2fc5f23667adf3bbaf2
Tried the following query which is getting executed successfully but not updating the option:
Model.findOneAndUpdate({
_id : ObjectId("5f8af2fc5f23667adf3bbaf2"),
"questions._id": ObjectId("5f8af30d5f23667adf3bbaf5"),
"questions.options._id": ObjectId("5f8af3115f23667adf3bbaf9"),
}, {
$set: {
"questions.$[q].options.$[o].order": data.order,
"questions.$[q].options.$[o].definition": data.definition,
"questions.$[q].options.$[o].type": data.type,
},
},
{
arrayFilters: [{ "q._id": ObjectId(qid) }, { "o._id": ObjectId(oid) }]
})
Is tihs possible to do in a single mongoose findOneAndUpdate method?
Your query is correct, I have just hardcoded object id values in array filter and it is updating the documents. I have updated name and desc as u said. Do try this out. One more thing in mongoose u have to specify the object id as "mongoose.Types.ObjectId".
Therefore in your case it would be like "q._id": mongoose.Types.ObjectId("5f8af3115f23667adf3bbaf8").
And one more thing is that you are using findAndUpdate, try using update only depending on your mongoose version
Here is mongoplayground:
https://mongoplayground.net/p/TP5iCTAC5R_
Query:
db.collection.update({
_id: ObjectId("5f8af2fc5f23667adf3bbaf2"),
"questions._id": ObjectId("5f8af3115f23667adf3bbaf8"),
"questions.options._id": ObjectId("5f8af3115f23667adf3bbaf9")
},
{
$set: {
"questions.$[q].options.$[o].name": "anotherName",
"questions.$[q].options.$[o].desc": "anotherDesc"
}
},
{
arrayFilters: [
{
"q._id": ObjectId("5f8af3115f23667adf3bbaf8")
},
{
"o._id": ObjectId("5f8af3115f23667adf3bbaf9")
}
]
})
Output :
[
{
"_id": ObjectId("5f8af2fc5f23667adf3bbaf2"),
"questions": [
{
"_id": ObjectId("5f8af30d5f23667adf3bbaf5"),
"desc": "some text"
},
{
"_id": ObjectId("5f8af3115f23667adf3bbaf8"),
"desc": "some text",
"options": [
{
"_id": ObjectId("5f8af3115f23667adf3bbaf9"),
"desc": "anotherDesc",
"name": "anotherName"
},
{
"_id": ObjectId("5f8af3115f23667adf3bbafa"),
"desc": "description 2",
"name": "some name"
}
]
}
],
"score": 2.5
}
]

Using Mongoose JS to get array of pointer values for document, and expand return

What I am trying to do is the following.
I have objects that could live in multiple documents, and although I am not sure if this is best practice, I am using their ObjectId as pointers.
Document A
{
"_id": {
"$oid": "51a02dade4b02780aeee5ab7"
},
"title": "My Document A",
"artifacts": [
"51a81d8ee4b084336fea2d33",
"asdfsdfe4b084336fea2d33"
]
}
Document B
{
"_id": {
"$oid": "51a02dade4b02780aeee5ab7"
},
"title": "My Document A",
"artifacts": [
"51a81d8ee4b084336fea2d33",
"123454b084336fea2d33"
]
}
The individual artifact looks something like. If this artifact changes then there is no need to update the documents that it lives in:
{
"_id": {
"$oid": "51a81d8ee4b084336fea2d33"
},
"title": "Artifact A",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
}
What I'd like to do, is get an expanded list of all the artifacts shown in either doc A or doc B in an array something like:
[{
"_id": {
"$oid": "51a81d8ee4b084336fea2d33"
},
"title": "Artifact A",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
},
{
"_id": {
"$oid": "123455e4b084336fea2d33"
},
"title": "Artifact B",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
}]
The way I have done it in the past is by POSTing from client a list of Ids and then using
{_id: { $in: userQuestArray} }
but if i am constantly posting back to node from the client it seems a bit inefficient. Especially if the array I am posting is hundreds of items long.
Mongoose provide you a feature population which exactly does the same thing you are looking for http://mongoosejs.com/docs/populate.html. In your case what you need to do is
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var tempSchema = Schema({
title : String,
artifacts : [{ type: Schema.Types.ObjectId, ref: 'Artifacts' }]
});
var artifactsSchema = Schema({
title : String,
media : { player: String, source: String, }
});
var tempModel = mongoose.model('Temp', tempSchema);
var artifactsModel = mongoose.model('Artifacts', artifactsSchema);
Now whenever artifacts documents gets created you need to push that in respective temp dcoument artifacts array as shown in mongoose documentation. You can retrive it like that
tempModel
.find(query)
.populate('artifacts')
.exec(function (err, results) {
if (err)
console.log(err);
console.log(results);
})
//results contains data in form shown below
[{
"_id": {
"$oid": "51a02dade4b02780aeee5ab7"
},
"title": "My Document A",
"artifacts": [{
"_id": {
"$oid": "51a81d8ee4b084336fea2d33"
},
"title": "Artifact A",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
},
{
"_id": {
"$oid": "123455e4b084336fea2d33"
},
"title": "Artifact B",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
}]
}]

Resources