Mongoose update not updating - node.js

I'm trying to update a document which has a specific ID with the current date/time but the below code is not resulting in the DB getting updated and no errors. Any help would be great, thanks.
Schema:
var MerchantShema = new Schema({
merchant_id: Number,
merchant_aw_id: Number,
merchant_name: String,
merchant_url: String,
merchant_image: String,
product_feed: String,
product_feed_updated: Date,
created_at: {type: Date, default: Date.now},
updated_at: {type: Date, default: Date.now}
});
Update Query:
updateMerchantLastProductUpdate: function (mID) {
now = new Date();
var query = { "merchant_aw_id" : mID };
Merchants.update(query, { "product_feed_updated": now }, function (err) {
if (err) return console.error(err);
})
}
Route
app.get('/queries', function (req, res) {
queries.updateMerchantLastProductUpdate("2926");
});
Example document
{
"_id": {
"$oid": "55997638e4b01f0391cb99aa"
},
"merchant_id": "0003",
"merchant_aw_id": "2926",
"merchant_name": "Multipower",
"merchant_url": "www.multipower.com/uk/",
"merchant_image": "",
"product_feed": "aw",
"product_feed_updated": "",
"created_at": "",
"updated_at": ""
}

The merchant_aw_id field in your mongoose schema is expecting a number so you need to parse the string for integer by using the parseInt() method in your query. You also need the $set update operator which replaces the value of a field with the specified value to update your document, together with the {multi: true} option which if set to true, updates multiple documents that meet the query criteria. If set to false, updates one document. The default value is false:
updateMerchantLastProductUpdate: function (mID) {
var now = new Date(),
query = { "merchant_aw_id" : parseInt(mID) },
update = {
"$set": { "product_feed_updated": now }
},
options = { "multi": true };
Merchants.update(query, update, options, function (err) {
if (err) return console.error(err);
})
}

My error was caused by my model have the ID I was looking for in the format Number but my data in mongoDB was a String

Related

MongoError: The positional operator did not find the match needed from the query. at Function.create()

I am trying to update values into an object array(users) if it does not already exist in MongoDB. Here is my Schema:
ownerid:{
type: Number,
required: 'This field is required'
},
name:{
type: String
},
capacity:{
type: Number
},
basePrice:{
type: Number
},
users:[{
id: Number,
price: Number,
target: Number,
frequency: Number
}],
filePath:{
type: String
},
status:{
type: String
}
});
The following is my router method:
app.post('/userBid',urlEncodedParser,function(req,res){
resName=req.body.resName;
console.log(resName);
Resource.find({"name":resName},{"users.id": userid},function(err,existingUser){
if (!existingUser){
console.log("already in queue");
//res.render('userHome.ejs');
}
else{
console.log("in update");
Resource.update({'name': resName},
{'$set': {
'users.$.frequency': 1,
'users.$.id': userid,
'users.$.price': req.body.price,
'users.$.target': req.body.target
}},{'multi': true},
function(err,model) {
if(err){
console.log(err);
return res.send(err);
}
return res.json(model);
});
}
});
});
I have tried using $push but that does not seem to work either. Also I can't use '0' instead of '$' as multiple users will be inserted by the users and I need to store them all.
Issue :
Reason why we use $ is to update a specific object/few specific objects in an array field that meet our condition. So when you use any positional operators like $ or $[] then in filter part of .update({filterPart},{updatePart}) query you need to use a filter to find specific object in array. So for example if id field is unique in users array then you can use it to filter/find the object needs to be updated.
Try this below code :
app.post("/userBid", urlEncodedParser, function (req, res) {
resName = req.body.resName;
console.log(resName);
/** Use findOne if `name` is unique.
* Cause `existingUser` will be array, instead findOne will return an object or null - So you can just do if(existingUser)to check true values */
Resource.find({ name: resName }, { "users.id": userid }, function (
err,
existingUser
) {
if (!existingUser) {
console.log("already in queue");
//res.render('userHome.ejs');
} else {
console.log("in update");
Resource.update(
{ name: resName, "users.id": userid }, /** `"users.id": userid` is the only change needed */
{
$set: {
"users.$.frequency": 1,
"users.$.id": userid,
"users.$.price": req.body.price,
"users.$.target": req.body.target,
},
},
{ multi: true },
function (err, model) {
if (err) {
console.log(err);
return res.send(err);
}
return res.json(model);
}
);
}
});
});

MongoDB - update data in array of objects within object

I have a document in mongoDB structured like that
_id: ObjectId("generatedByMongo"),
name: {
required: true,
type: String,
trim: true
},
last: {
required: true,
type: String,
trim: true
},
grades: [{
grade: {
_id: ObjectId(""),
grade: Number,
date: date
}
}]
And to server I send array of objects containing 3 fields
[
{studentId}, {gradeId}, {newGrade}
]
What I'm trying to accomplish is I want to find in within that user collection grade with given gradeId and update it's value to newGrade. As far as I tried to do that I have done this
router.patch('/students/updateGrade',async(req,res) => {
const studentId = req.body.updateGradeArray[0].studentId;
const gradeId = req.body.updateGradeArray[0].gradeId;
const newGrade = req.body.updateGradeArray[0].newGrade;
try {
const student = await Student.find({_id: studentId})
.select({'grades': {$elemMatch: {_id: gradeId}}});
} catch(e) {
console.log(e);
}
}
);
If you intend to update just grade.grade(the number value), try this:
Student.updateOne(
// Find a document with _id matching the studentId
{ "_id": studentId },
// Update the student grade
{ $set: { "grades.$[selectedGrade].grade": newGrade } },
{ arrayFilters: [{ "selectedGrade._id": gradeId }] },
)
Why this should work:
Since you are trying to update a student document, you should be using one of MongoDB update methods not find. In the query above, I'm using the updateOne method. Inside the updateOne, I am using a combination of $set and $[identifier] update operators to update the student grade.
I hope this helps✌🏾

MongoDB aggregate, geNear and iterate over callback

I have a problem and I can´t find a solution. I have some MongoSchemas where I store Geolocation from users. Mobile Phone is sending me longitude and latitude every 5 minutes. This API is working perfectly.
Mongo-Schema looks like:
// Importing Node packages required for schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
//= ===============================
// User Schema
//= ===============================
const GeolocationSchema = new Schema({
loc: {
type: { type: String },
coordinates: { type: [Number], index: '2dsphere' }
},
user: { type: Schema.Types.ObjectId, ref: 'User' }
},
{
timestamps: true
});
module.exports = mongoose.model('Geolocation', GeolocationSchema);
Now, I want to calculate users-nearby which have an "updateAt"-timestamp not even longer than 5 minutes in the past. That means that one or more users can be in a distance of e.g. 500m until 5 minutes in the past. This should be a match. For this I use Mongo aggregate, and I want to iterate the callback-result and extract the user._id out of the result to build a match.
This is what I tried:
const Geolocation = require('../models/geolocation')
User = require('../models/user'),
config = require('../config/main');
exports.setGeolocation = function (req, res, next) {
// Only return one message from each conversation to display as snippet
console.log(req.user._id);
var geoUpdate = Geolocation.findOneAndUpdate( { user: req.user._id },
{ loc: {
type: 'Point',
coordinates: req.body.coordinates.split(',').map(Number)
},
user: req.user._id
},
{upsert: true, new: true, runValidators: true}, // options
function (err, doc) { // callback
if (err) {
console.log(err);
}
});
// create dates for aggregate query
var toDate = new Date( (new Date()).getTime());
var fromDate = new Date( (new Date()).getTime() - 5000 * 60 );
var match = Geolocation.aggregate([
{
$geoNear: {
near: {
type: "Point",
coordinates: req.body.coordinates.split(',').map(Number)
},
distanceField: "dist.calculated",
maxDistance: 500,
includeLocs: "dist.location",
uniqueDocs: true,
query: { user: {$ne: req.user._id } , updatedAt: { $gte: fromDate,$lte: toDate }},
num: 5,
spherical: true
}
}], function (err, doc){
//here I´m going in trouble correctly parsing doc
var str = JSON.stringify(doc);
var newString = str.substring(1, str.length-1);
var response = JSON.parse(newString);
console.log(response.user);
});
res.sendStatus(200);
};
As you can see I´m going in trouble in parsing the "doc"-callback to iterate over the documents. If I want to parse it as jSON I´m getting an token-error on position 1. If I have more than 2 results, I´m getting an error on position 288.
That´s why I tried to parse and stringify the "doc". But this is not working correctly.
Maybe, someone could help me with a solution. I´m not familiar with mongo-functions because I´m starting with it, maybe there is a better solution but I can´t find something else to calculate geoNear and iterate afterwards over the results.
Thx at all who can help...

How to join two collections in mongoose

I have two Schema defined as below:
var WorksnapsTimeEntry = BaseSchema.extend({
student: {
type: Schema.ObjectId,
ref: 'Student'
},
timeEntries: {
type: Object
}
});
var StudentSchema = BaseSchema.extend({
firstName: {
type: String,
trim: true,
default: ''
// validate: [validateLocalStrategyProperty, 'Please fill in your first name']
},
lastName: {
type: String,
trim: true,
default: ''
// validate: [validateLocalStrategyProperty, 'Please fill in your last name']
},
displayName: {
type: String,
trim: true
},
municipality: {
type: String
}
});
And I would like to loop thru each student and show it's time entries. So far I have this code which is obviously not right as I still dont know how do I join WorksnapTimeEntry schema table.
Student.find({ status: 'student' })
.populate('student')
.exec(function (err, students) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
}
_.forEach(students, function (student) {
// show student with his time entries....
});
res.json(students);
});
Any one knows how do I achieve such thing?
As of version 3.2, you can use $lookup in aggregation pipeline to perform left outer join.
Student.aggregate([{
$lookup: {
from: "worksnapsTimeEntries", // collection name in db
localField: "_id",
foreignField: "student",
as: "worksnapsTimeEntries"
}
}]).exec(function(err, students) {
// students contain WorksnapsTimeEntries
});
You don't want .populate() here but instead you want two queries, where the first matches the Student objects to get the _id values, and the second will use $in to match the respective WorksnapsTimeEntry items for those "students".
Using async.waterfall just to avoid some indentation creep:
async.waterfall(
[
function(callback) {
Student.find({ "status": "student" },{ "_id": 1 },callback);
},
function(students,callback) {
WorksnapsTimeEntry.find({
"student": { "$in": students.map(function(el) {
return el._id
})
},callback);
}
],
function(err,results) {
if (err) {
// do something
} else {
// results are the matching entries
}
}
)
If you really must, then you can .populate("student") on the second query to get populated items from the other table.
The reverse case is to query on WorksnapsTimeEntry and return "everything", then filter out any null results from .populate() with a "match" query option:
WorksnapsTimeEntry.find().populate({
"path": "student",
"match": { "status": "student" }
}).exec(function(err,entries) {
// Now client side filter un-matched results
entries = entries.filter(function(entry) {
return entry.student != null;
});
// Anything not populated by the query condition is now removed
});
So that is not a desirable action, since the "database" is not filtering what is likely the bulk of results.
Unless you have a good reason not to do so, then you probably "should" be "embedding" the data instead. That way the properties like "status" are already available on the collection and additional queries are not required.
If you are using a NoSQL solution like MongoDB you should be embracing it's concepts, rather than sticking to relational design principles. If you are consistently modelling relationally, then you might as well use a relational database, since you won't be getting any benefit from the solution that has other ways to handle that.
It is late but will help many developers.
Verified with
"mongodb": "^3.6.2",
"mongoose": "^5.10.8",
Join two collections in mongoose
ProductModel.find({} , (err,records)=>{
if(records)
//reurn records
else
// throw new Error('xyz')
})
.populate('category','name') //select only category name joined collection
//.populate('category') // Select all detail
.skip(0).limit(20)
//.sort(createdAt : '-1')
.exec()
ProductModel Schema
const CustomSchema = new Schema({
category:{
type: Schema.ObjectId,
ref: 'Category'
},
...
}, {timestamps:true}, {collection: 'products'});
module.exports = model('Product',CustomSchema)
Category model schema
const CustomSchema = new Schema({
name: { type: String, required:true },
...
}, {collection: 'categories'});
module.exports = model('Category',CustomSchema)

How to update mixed type field in Mongoose without overwriting the current data?

I have the following schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ShopSchema = new Schema({
name: Schema.Types.Mixed,
country: {
type: String,
default: ''
},
createdAt: {
type: Date,
default: Date.now
},
defaultLanguage: {
type: String
},
account: {type : Schema.ObjectId, ref : 'Account'},
});
mongoose.model('Shop', ShopSchema);
"name" field is multilingual. I mean, I will keep the multilingual data like
name: {
"en": "My Shop",
"es": "Mi Tienda"
}
My problem is, in a controller, I am using this code to update the shop:
var mongoose = require('mongoose')
var Shop = mongoose.model('Shop')
exports.update = function(req, res) {
Shop.findByIdAndUpdate(req.params.shopid, {
$set: {
name: req.body.name
}
}, function(err, shop) {
if (err) return res.json(err);
res.json(shop);
});
};
and it is obvious that new data overrides the old data. What I need is to extend the old data with the new one.
Is there any method to do that?
You should to use the method .markModified(). See the doc http://mongoosejs.com/docs/schematypes.html#mixed
Since it is a schema-less type, you can change the value to anything else you like, but Mongoose loses the ability to auto detect and save those changes. To "tell" Mongoose that the value of a Mixed type has changed, call the .markModified(path) method of the document passing the path to the Mixed type you just changed.
person.anything = { x: [3, 4, { y: "changed" }] };
person.markModified('anything');
person.save(); // anything will now get saved
Use "dot notation" for the specific element:
Shop.findByIdAndUpdate(req.params.shopid, {
"$set": {
"name.en": req.body.name
}
}, function(err, shop) {
if (err) return res.json(err);
res.json(shop);
});
});
That wil either only overwrite the "en" element if that is what you want to do or "create" a new element with the data you set it to. So if you used "de" and that did not exist there will be the other elements and a new "de" one with the value.

Resources