How do I update an array using an object in mongodb? - mongodb

I try to add an geojson object to an existing array in mongodb, this is my object that I'd like to add:
const location = {
type: "Feature",
properties: {
description: place.address,
name: place.name
},
geometry: {
coordinates: [
place.latLng.latitude,
place.latLng.longitude
],
type: "Point"
},
userIds: [userId],
id: place.id
}
I tried using this mongodb call without any effect:
db.collection.updateOne(
{ _id: "5e6e32051c9d4400128cba9c" },
{ $push: { features: location } },
function(err, result) {
if (err) {
reject(err);
}
console.log(result);
console.log("Added new location successfully");
resolve(true);
});
This does nothing. Features is an array which should contain geojson objects.
What do I do wrong?

Ok, I found the answer on this page: https://www.quora.com/How-do-I-update-a-document-in-mongodb-using-_id-as-query-parameter
In order to query for an _id you apparently have to convert the _id into an ObjectId first.
So I did this here:
const ObjectID = require('mongodb').ObjectID;
const id = ObjectID("5e6e32051c9d4400128cba9c");
And then:
db.collection.updateOne(
{ _id: id },
{ $push: { features: location } },
function(err, result) {
if (err) {
reject(err);
}
console.log(result);
console.log("Added new location successfully");
resolve(true);
});
This did work! :)

Related

Push an object into a nested array in MongoDB

I've got a head-scratcher here that I'd like to share with you all.
So here's the model:
_id: ObjectId()
name: String,
columns: [
{
name: String,
_id: ObjectId()
tasks: [
{
title: String,
description: String,
status: String,
_id: ObjectId()
subtasks: [
{
title: String,
isCompleted: Boolean,
},
],
},
],
},
],
});
and the query:
exports.createSubtask = (req, res) => {
if (!req.body) {
res.status(400).send({ message: "Task name can not be empty!" });
return;
}
const board = req.params.board;
const column = req.params.column;
const task = req.params.task;
Board.findOneAndUpdate(
{
_id: board,
"columns._id": column,
"columns.tasks._id": task,
},
{
$push: {
"columns.$.tasks.$.subtasks": req.body,
},
}
)
.then((data) => {
if (!data) {
res.status(404).send({
message: `Cannot update Task with id=${task}. Maybe task was not found!`,
});
} else res.send({ message: "Task was updated successfully." });
})
.catch((err) => {
res.status(500).send({
message: "Error updating Task with id=" + task,
});
});
};
I'm trying to push an object into the subtasks array with $push, but Postman is throwing an error.
Any ideas as to what I'm doing wrong? Appreciate the help.
Golden Ratio
However, I was able to successfully push an object into the tasks array with the following query:
exports.createTask = (req, res) => {
if (!req.body) {
res.status(400).send({ message: "Task name can not be empty!" });
return;
}
const board = req.params.board;
const column = req.params.column;
Board.findOneAndUpdate(
{
_id: board,
"columns._id": column,
},
{
$push: {
"columns.$.tasks": req.body,
},
}
)
.then((data) => {
if (!data) {
res.status(404).send({
message: `Cannot update Column with id=${column}. Maybe column was not found!`,
});
} else res.send({ message: "Column was updated successfully." });
})
.catch((err) => {
res.status(500).send({
message: "Error updating Column with id=" + column,
});
});
};
It is not possible to use multiple positional $ for the nested array as mention in docs:
The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single value
You should work with the positional filtered operator $[<identifier>].
Board.findOneAndUpdate(
{
_id: board,
"columns._id": column,
"columns.tasks._id": task,
},
{
$push: {
"columns.$.tasks.$[task].subtasks": req.body,
},
},
{
arrayFilters: [
{ "task._id": task }
]
}
)
.then(...);
Note: Ensure that the passed in task is ObjectId type.
Credit to Yong Shun Yong for the help. Through trial and error, I solved the problem with the following code
Board.findOneAndUpdate(
{
_id: board,
"columns._id": column,
},
{
$push: {
"columns.$.tasks.$[].subtasks": req.body,
},
},
{
arrayFilters: [{ "task._id": task }],
}
)

Mongoose - Update/Find Specific Object in an Array Not Working As Expected

I am following the docs without luck and am at a standstill while trying to update an object in an object in an array using MongoDB and Mongoose.
Here is my document:
{
fields: [
{ id: 603d63086db2db00ab09f50f, data: [Object] },
{ id: 603d63086db2db00ab09f510, data: [Object] },
{ id: 603d63086db2db00ab09f511, data: [Object] },
{ id: 603d63086db2db00ab09f512, data: [Object] },
{ id: 603d63086db2db00ab09f513, data: [Object] },
{ id: 603d63086db2db00ab09f514, data: [Object] },
{ id: 603d63086db2db00ab09f515, data: [Object] }
],
layouts: [],
_id: 603d631a6db2db00ab09f517,
bandId: '603d63146db2db00ab09f516',
eventType: 'private',
ownerId: '6039354906410800c14934c1',
__v: 0
}
I am trying to updateOne of the fields.data in the fields array. fields.data is an object as well.
I call my Express/Node Backend to this route.
//Update
router.put("/:id", async (req, res) => {
try {
let updating = await QuoteGenerator.updateOne(
{ _id: req.params.id, "fields.id": req.body.id },
{
"$set": {
"fields.$.data": req.body.data,
},
}
);
let item = await QuoteGenerator.findOne({ _id: req.params.id });
res.json({ success: "Item Updated.", item });
} catch (err) {
console.log(err);
res.json({ error: "Something went wrong when updating this item." });
}
});
Where req.body is:
{ id: '603d63086db2db00ab09f50f', data: { type: 1, rate: '200.30' } }
**Just in case it's helpful, here is what one of the fields objects looks like in the document,
{"id":"603d63086db2db00ab09f50f","data":{"type":1,"rate":300}}
I have even tried changing my route to find this document - which I have confirmed exists - Truly at a loss why it won't find the document.
Here is how I changed the above route to find the document.
//Update
router.put("/:id", async (req, res) => {
try {
let updating = await QuoteGenerator.find(
{ _id: req.params.id, "fields.id": req.body.id },
);
console.log(updating) //returns []
let item = await QuoteGenerator.findOne({ _id: req.params.id });
res.json({ success: "Item Updated.", item });
} catch (err) {
console.log(err);
res.json({ error: "Something went wrong when updating this item." });
}
});
The Model
//Create Schema - QG
const QuoteGeneratorSchema = new Schema({
bandId: {
type: String,
required: true,
},
ownerId: {
type: String,
required: true,
},
fields: {
type: Array,
default: defaultFields,
required: true,
},
eventType: {
type: String,
required: false,
},
layouts: {
type: Array,
required: false,
},
});
let QuoteGenerator = mongoose.model("QuoteGenerator", QuoteGeneratorSchema);
module.exports = QuoteGenerator;
Any nudge in the right direction to replacing that data object with a new data object would be extremely helpful! Thanks!

Mongoose pull ObjectId from array

i'm trying to do a pretty simple operation, pull an item from an array with Mongoose on a Mongo database like so:
User.update({ _id: fromUserId }, { $pull: { linkedUsers: [idToDelete] } });
fromUserId & idToDelete are both Objects Ids.
The schema for Users goes like this:
var UserSchema = new Schema({
groups: [],
linkedUsers: [],
name: { type: String, required: true, index: { unique: true } }
});
linkedUsers is an array that only receives Ids of other users.
I've tried this as well:
User.findOne({ _id: fromUserId }, function(err, user) {
user.linkedUsers.pull(idToDelete);
user.save();
});
But with no luck.
The second option seem to almost work when i console the lenghts of the array at different positions but after calling save and checking, the length is still at 36:
User.findOne({ _id: fromUserId }, function(err, user) {
console.log(user.linkedUsers.length); // returns 36
user.linkedUsers.pull(idToDelete);
console.log(user.linkedUsers.length); // returns 35
user.save();
});
So it looks like i'm close but still, no luck. Both Ids are sent via the frontend side of the app.
I'm running those versions:
"mongodb": "^2.2.29",
"mongoose": "^5.0.7",
Thanks in advance.
You need to explicitly define the types in your schema definition i.e.
groups: [{ type: Schema.Types.ObjectId, ref: 'Group' }],
linkedUsers: [{ type: Schema.Types.ObjectId, ref: 'User' }]
and then use either
User.findOneAndUpdate(
{ _id: fromUserId },
{ $pullAll: { linkedUsers: [idToDelete] } },
{ new: true },
function(err, data) {}
);
or
User.findByIdAndUpdate(fromUserId,
{ $pullAll: { linkedUsers: [idToDelete] } },
{ new: true },
function(err, data) {}
);
I had a similar issue. I wanted to delete an object from an array, using the default _id from mongo, but my query was wrong:
const update = { $pull: { cities: cityId }};
It should be:
const update = { $pull: { cities: {_id: cityId} }};

MongoError when trying to aggregate with collection relationship

I am new to mongodb, using Mean stack from meanjs.org.
I have a model with a user collection relationship:
var MealSchema = new Schema({
mealDate: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Meal', MealSchema);
I had find an implementation that works:
var user = req.user;
Meal.find({
mealDate: {
$gte: minus8days.toDate(),
$lt: tomorrow.toDate()
},
user : user
}).exec(function (err, meals) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(meals);
}
});
But I need some grouping and aggregation so I am trying to change this code to use aggregate instead of a find, I've tried many combinations and I keep getting an error, basically the following implementation throws an error MongoError: Maximum call stack size exceeded
Meal.aggregate([
{
$match: {
$and: [
{
mealDate: {
$gte: minus8days.toDate(),
$lt: tomorrow.toDate()
}
}, { user: user }
]
}
}]...
Why user:user works with find and not with aggregate?
How can the aggregate approach be fixed?
Try this :
Meal.aggregate([
{
$match: {
mealDate: { $gte: minus8days.toDate(), $lt: tomorrow.toDate()},
user: user._id
}
}
], function(err, docs){
if(err) console.log("Error : " + JSON.stringify(err));
if(docs) console.log("Got the docs successfully : " + JSON.stringify(docs));
});

How to update embedded document in mongoose?

I've looked through the mongoose API, and many questions on SO and on the google group, and still can't figure out updating embedded documents.
I'm trying to update this particular userListings object with the contents of args.
for (var i = 0; i < req.user.userListings.length; i++) {
if (req.user.userListings[i].listingId == req.params.listingId) {
User.update({
_id: req.user._id,
'userListings._id': req.user.userListings[i]._id
}, {
'userListings.isRead': args.isRead,
'userListings.isFavorite': args.isFavorite,
'userListings.isArchived': args.isArchived
}, function(err, user) {
res.send(user);
});
}
}
Here are the schemas:
var userListingSchema = new mongoose.Schema({
listingId: ObjectId,
isRead: {
type: Boolean,
default: true
},
isFavorite: {
type: Boolean,
default: false
},
isArchived: {
type: Boolean,
default: false
}
});
var userSchema = new mongoose.Schema({
userListings: [userListingSchema]
});
This find also doesn't work, which is probably the first issue:
User.find({
'_id': req.user._id,
'userListings._id': req.user.userListings[i]._id
}, function(err, user) {
console.log(err ? err : user);
});
which returns:
{ stack: [Getter/Setter],
arguments: [ 'path', undefined ],
type: 'non_object_property_call',
message: [Getter/Setter] }
That should be the equivalent of this mongo client call:
db.users.find({'userListings._id': ObjectId("4e44850101fde3a3f3000002"), _id: ObjectId("4e4483912bb87f8ef2000212")})
Running:
mongoose v1.8.1
mongoose-auth v0.0.11
node v0.4.10
when you already have the user, you can just do something like this:
var listing = req.user.userListings.id(req.params.listingId);
listing.isRead = args.isRead;
listing.isFavorite = args.isFavorite;
listing.isArchived = args.isArchived;
req.user.save(function (err) {
// ...
});
as found here: http://mongoosejs.com/docs/subdocs.html
Finding a sub-document
Each document has an _id. DocumentArrays have a special id method for looking up a document by its _id.
var doc = parent.children.id(id);
* * warning * *
as #zach pointed out, you have to declare the sub-document's schema before the actual document 's schema to be able to use the id() method.
Is this just a mismatch on variables names?
You have user.userListings[i].listingId in the for loop but user.userListings[i]._id in the find.
Are you looking for listingId or _id?
You have to save the parent object, and markModified the nested document.
That´s the way we do it
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Profile.findById(req.params.id, function (err, profile) {
if (err) { return handleError(res, err); }
if(!profile) { return res.send(404); }
var updated = _.merge(profile, req.body);
updated.markModified('NestedObj');
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, profile);
});
});
};