I am building an API to store friends names for a game, I have built the API to receive the post request as so :
exports.addFriends = async (req, res) => {
try {
console.log('hit');
console.log(req.body.friendNames);
const addUser = await User.updateOne(
{ uniqueid: req.body.uniqueid },
{ $push: { friendNames: [req.body.friendNames] } }
);
res.json({
addUser
});
} catch (error) {
console.log(error);
}
};
ad the post request as
const friends = await axios.post('/api/v1/users/add/friends', {
uniqueId: this.uniqueid,
friendNames: [
{
userName: 'test',
region: 'euw'
}
]
});
My API is being hit as a see the logs, but no record is made. My User Schema is as so
const userSchema = new mongoose.Schema({
uniqueid: {
type: String,
required: true,
trim: true
},
summonerName: {
type: String
},
friendNames: [
{
userName: String,
region: String
}
]
});
I get no error and the request seems to go through, but no records are added. Any ideas?
$push is used to add one element to the array. But using the $each array update operator, we can push an array of items.
Also, I used findOneAndUpdate with new:true option to retrieve the updated document, because updateOne doesn't return the updated document.
exports.addFriends = async (req, res) => {
try {
console.log(req.body.friendNames);
const addUser = await User.findOneAndUpdate(
{ uniqueid: req.body.uniqueid },
{ $push: { friendNames: { $each: req.body.friendNames } } },
{ new: true }
);
res.json({ addUser });
} catch (error) {
console.log(error);
res.status(500).send("Something went wrong");
}
}
Let's say we have this existing document:
{
"_id": "5e31c749f26d5f242c69f3aa",
"uniqueid": "uniqueid1",
"summonerName": "John",
"friendNames": [
{
"_id": "5e31c749f26d5f242c69f3ab",
"userName": "Max",
"region": "Germany"
}
],
"__v": 0
}
Let's send a request to the controller with this request body:
{
"uniqueid": "uniqueid1",
"friendNames": [
{
"userName": "Andrew",
"region": "England"
},
{
"userName": "Smith",
"region": "USA"
}
]
}
The response will be like this:
{
"addUser": {
"_id": "5e31c749f26d5f242c69f3aa",
"uniqueid": "uniqueid1",
"summonerName": "John",
"friendNames": [
{
"_id": "5e31c749f26d5f242c69f3ab",
"userName": "Max",
"region": "Germany"
},
{
"_id": "5e31c763f26d5f242c69f3ad",
"userName": "Andrew",
"region": "England"
},
{
"_id": "5e31c763f26d5f242c69f3ac",
"userName": "Smith",
"region": "USA"
}
],
"__v": 0
}
}
Related
i am receiving a string "city" from "req.body"
my function:
async function searchProject(req: Request, res: Response) {
const { code, archiveNumber, air, city, municipality, origin } = req.body;
console.log(city);
try {
const projects = await Project.find({
$or: [
{ code: { $regex: code ?? "" } },
{ archiveNumber },
{ air },
{
city: {
$in: [city, "$city.value"],
},
},
{ municipality },
{ origin },
],
});
if (!projects)
return res.status(400).json({ message: "Pas de projet trouvée" });
res.status(200).json(projects);
} catch (err) {
console.log(err);
res.status(500).json({ message: "Server Error" });
}
}
i am using $or operator to get projects that matches at least on the values that receive from "req.body", all other values seem to work but "city".
in my document, here is how city looks like:
"city": [
{
"id": "62ed0121f58a5ed78ac05a85",
"value": "City 1"
}
],
now how can i compare a "city" which i get from "req.body" with "city.value" ?
Solved!!!
i tried:
const projects = await Project.find({
$or: [
{ code: { $regex: code ?? "" } },
{ archiveNumber },
{ air },
{ municipality },
{ origin },
],
}).elemMatch("city", {
value: city,
});
and it worked
thanks to
Siddhesh Khadapkar
I have a document that looks something like this
{
"_id":{
"$oid":"id"
},
"side1":[
{
"username":"test1",
"id":"id1",
},
{
"username":"test2",
"id":"id2",
},
{
"username":"test3",
"id":"id3",
}
],
"side2":[
{
"username":"test4",
"id":"id4",
},
{
"username":"test5",
"id":"id5",
},
{
"username":"test6",
"id":"id6",
}
],
}
I want to be able to search and update one of the sides, for example, if I searched with username for side1 and that username would be there then I would be able to $set other fields for the object with this username. Something like: Search side1 username test1: $set result.id: "43242342" this would set the id of the object with the username of test1 to 43242342. I am not sure on how I would go about doing this, I have tried using $elemMatch but that didn't bring any results.
Test.findOne({ id: id },
{ side1: { $elemMatch: {username: username} } }, function (err, result) {
if (err) {
console.log(err);
} else {
console.log(result)
}
});
I'm not exactly sure how you want to update the document, but perhaps this is what you are looking for?
db.collection.update({
"_id": ObjectId("000000000000000000000001"),
"side1.username": "test1"
},
{
"$set": {
"side1.$.id": "43242342"
}
})
Try it on mongoplayground.net.
Example updated document:
[
{
"_id": ObjectId("000000000000000000000001"),
"side1": [
{
"id": "43242342",
"username": "test1"
},
{
"id": "id2",
"username": "test2"
},
{
"id": "id3",
"username": "test3"
}
],
"side2": [
{
"id": "id4",
"username": "test4"
},
{
"id": "id5",
"username": "test5"
},
{
"id": "id6",
"username": "test6"
}
]
}
]
Could you do something like this?
function addProperty(id, side, username, property, value) {
const query = {
_id: {
$oid: id,
},
};
const update = {
$push: {
[side]: {
username: username,
[property]: value,
},
},
};
const options = {
upsert: true,
};
db.collection("Test").updateOne(query, update, options);
}
I have a board object and I want to edit it from express and mongoose, this is my object:
"boardMembers": [
"5f636a5c0d6fa84be48cc19d",
],
"boardLists": [
{
"cards": [
{
"_id": "5f7c9b77eb751310a41319ab",
"text": "card one"
},
{
"_id": "5f7c9bb524dd8d42d469bba3",
"text": "card two"
}
],
"_id": "5f7c9b6b02c19f21a493cb7d",
"title": "list one",
"__v": 0
}
],
"_id": "5f63877177beba2e3c15d159",
"boardName": "board1",
"boardPassword": "123456",
"boardCreator": "5f636a5c0d6fa84be48cc19d",
"g_createdAt": "2020-09-17T15:57:37.616Z",
"__v": 46
}
Now this is my code, I tried to do it with $pull, but nothing happend when I check it on Postman
router.put("/delete-task/:list/:task", auth, boardAuth, async (req, res) => {
const listId = req.params.list;
const task = req.params.task;
const board = await Board.findOne({ _id: req.board._id });
if (!board) return res.status(404).send("no such board");
Board.findOneAndUpdate(
{ _id: req.board._id },
{ $pull: { "boardLists.$[outer].cards": { _id: task } } },
{
arrayFilters: [{ "outer._id": listId }],
}
);
await board.save();
res.send(board);
});
what I am missing here?
Hope this will work in your case, you just need to convert your ids into mongo objectId. So your code will look something like this:
import mongoose from "mongoose";
const task = mongoose.Types.ObjectId(req.params.task);
const listId = mongoose.Types.ObjectId(req.params.list);
board = await Board.findOneAndUpdate(
{ _id: req.board._id},
{ $pull: {
"boardLists.$[outer].cards": { _id: task }
}
},
{
arrayFilters: [{ "outer._id": listId }],
returnNewDocument: true
}
);
res.send(board);
I use Nodejs, Hapijs and Mongoose.
I 've a schema and model as follows.
var schema = {
name: {
type: String,
required: true
},
lectures: {}
};
var mongooseSchema = new mongoose.Schema(schema, {
collection: "Users"
});
mongoose.model("Users", mongooseSchema);
For some reason, I need to keep "lectures"
as mixed type.
While saving/creating a document I create a nested property lectures.physics.topic[] where topic is an array.
Now, I'm trying to add/push a new object to "lectures.physics.topic" using $addToSet or $push.
userModel.findByIdAndUpdateAsync(user._id, {
$addToSet: {
"lectures.physics.topic": {
"name": "Fluid Mechanics",
"day": "Monday",
"faculty": "Nancy Wagner"
}
}
});
But the document is simply not getting updated. I tried using $push too. Nothing worked. What could be the problem?
I tried to another approach using mongoclient , to update the db directly .It works please find the below code which works
db.collection("Users").update({
"_id": user._id
}, {
$addToSet: {
"lectures.physics.topic": {
"name": "Fluid Mechanics",
"day": "Monday",
"faculty": "Nancy Wagner"
}
}
}, function(err, result) {
if (err) {
console.log("Superman!");
console.log(err);
return;
}
console.log(result);
});
I have to start the mongo client every time a request is hit.This is not a feasible solution.
Mongoose loses the ability to auto detect and save changes made on Mixed types so you need to "tell" it that the value of a Mixed type has changed by calling the .markModified(path) method of the document passing the path to the Mixed type you just changed:
doc.mixed.type = 'changed';
doc.markModified('mixed.type');
doc.save() // changes to mixed.type are now persisted
In your case, you could use findById() method to make your changes by calling the addToSet() method on the topic array and then triggering the save() method to persist the changes:
userModel.findById(user._id, function (err, doc){
var item = {
"name": "Fluid Mechanics",
"day": "Monday",
"faculty": "Nancy Wagner"
};
doc.lectures.physics.topic.addToSet(item);
doc.markModified('lectures');
doc.save() // changes to lectures are now persisted
});
I'd be calling "bug" on this. Mongoose is clearly doing the wrong thing as can be evidenced in the logging as shown later. But here is a listing that calls .findOneAndUpdate() from the native driver with the same update you are trying to do:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/school');
mongoose.set('debug',true);
var userSchema = new Schema({
name: {
type: String,
required: true
},
lectures: { type: Schema.Types.Mixed }
});
var User = mongoose.model( "User", userSchema );
function logger(data) {
return JSON.stringify(data, undefined, 2);
}
async.waterfall(
[
function(callback) {
User.remove({},function(err) {
callback(err);
});
},
function(callback) {
console.log("here");
var user = new User({ "name": "bob" });
user.save(function(err,user) {
callback(err,user);
});
},
function(user,callback) {
console.log("Saved: %s", logger(user));
User.collection.findOneAndUpdate(
{ "_id": user._id },
{
"$addToSet": {
"lectures.physics.topic": {
"name": "Fluid Mechanics",
"day": "Monday",
"faculty": "Nancy Wagner"
}
}
},
{ "returnOriginal": false },
function(err,user) {
callback(err,user);
}
);
}
],
function(err,user) {
if (err) throw err;
console.log("Modified: %s", logger(user));
mongoose.disconnect();
}
);
This works perfectly with the result:
Saved: {
"__v": 0,
"name": "bob",
"_id": "55cda1f5b5ee8b870e2f53bd"
}
Modified: {
"lastErrorObject": {
"updatedExisting": true,
"n": 1
},
"value": {
"_id": "55cda1f5b5ee8b870e2f53bd",
"name": "bob",
"__v": 0,
"lectures": {
"physics": {
"topic": [
{
"name": "Fluid Mechanics",
"day": "Monday",
"faculty": "Nancy Wagner"
}
]
}
}
},
"ok": 1
}
You neeed to be careful here as native driver methods are not aware of the connection status like the mongoose methods are. So you need to be sure a connection has been made by a "mongoose" method firing earlier, or wrap your app in a connection event like so:
mongoose.connection.on("connect",function(err) {
// start app in here
});
As for the "bug", look at the logging output from this listing:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/school');
mongoose.set('debug',true);
var userSchema = new Schema({
name: {
type: String,
required: true
},
lectures: { type: Schema.Types.Mixed }
});
var User = mongoose.model( "User", userSchema );
function logger(data) {
return JSON.stringify(data, undefined, 2);
}
async.waterfall(
[
function(callback) {
User.remove({},function(err) {
callback(err);
});
},
function(callback) {
console.log("here");
var user = new User({ "name": "bob" });
user.save(function(err,user) {
callback(err,user);
});
},
function(user,callback) {
console.log("Saved: %s", logger(user));
User.findByIdAndUpdate(
user._id,
{
"$addToSet": {
"lectures.physics.topic": {
"name": "Fluid Mechanics",
"day": "Monday",
"faculty": "Nancy Wagner"
}
}
},
{ "new": true },
function(err,user) {
callback(err,user);
}
);
}
],
function(err,user) {
if (err) throw err;
console.log("Modified: %s", logger(user));
mongoose.disconnect();
}
);
And the logged output with mongoose logging:
Mongoose: users.remove({}) {}
here
Mongoose: users.insert({ name: 'bob', _id: ObjectId("55cda2d2462283c90ea3f1ad"), __v: 0 })
Saved: {
"__v": 0,
"name": "bob",
"_id": "55cda2d2462283c90ea3f1ad"
}
Mongoose: users.findOne({ _id: ObjectId("55cda2d2462283c90ea3f1ad") }) { new: true, fields: undefined }
Modified: {
"_id": "55cda2d2462283c90ea3f1ad",
"name": "bob",
"__v": 0
}
So in true "What the Fudge?" style, there is a call there to .findOne()? Which is not what was asked. Moreover, nothing is altered in the database of course because the wrong call is made. So even the { "new": true } here is redundant.
This happens at all levels with "Mixed" schema types.
Personally I would not nest within "Objects" like this, and just make your "Object keys" part of the standard array as additional properties. Both MongoDB and mongoose are much happier with this, and it is much easier to query for information with such a structure.
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/school');
mongoose.set('debug',true);
var lectureSchema = new Schema({
"subject": String,
"topic": String,
"day": String,
"faculty": String
});
var userSchema = new Schema({
name: {
type: String,
required: true
},
lectures: [lectureSchema]
});
var User = mongoose.model( "User", userSchema );
function logger(data) {
return JSON.stringify(data, undefined, 2);
}
async.waterfall(
[
function(callback) {
User.remove({},function(err) {
callback(err);
});
},
function(callback) {
console.log("here");
var user = new User({ "name": "bob" });
user.save(function(err,user) {
callback(err,user);
});
},
function(user,callback) {
console.log("Saved: %s", logger(user));
User.findByIdAndUpdate(
user._id,
{
"$addToSet": {
"lectures": {
"subject": "physics",
"topic": "Fluid Mechanics",
"day": "Monday",
"faculty": "Nancy Wagner"
}
}
},
{ "new": true },
function(err,user) {
callback(err,user);
}
);
}
],
function(err,user) {
if (err) throw err;
console.log("Modified: %s", logger(user));
mongoose.disconnect();
}
);
Output:
Mongoose: users.remove({}) {}
here
Mongoose: users.insert({ name: 'bob', _id: ObjectId("55cda4dc40f2a8fb0e5cdf8b"), lectures: [], __v: 0 })
Saved: {
"__v": 0,
"name": "bob",
"_id": "55cda4dc40f2a8fb0e5cdf8b",
"lectures": []
}
Mongoose: users.findAndModify({ _id: ObjectId("55cda4dc40f2a8fb0e5cdf8b") }) [] { '$addToSet': { lectures: { faculty: 'Nancy Wagner', day: 'Monday', topic: 'Fluid Mechanics', subject: 'physics', _id: ObjectId("55cda4dc40f2a8fb0e5cdf8c") } } } { new: true, upsert: false, remove: false }
Modified: {
"_id": "55cda4dc40f2a8fb0e5cdf8b",
"name": "bob",
"__v": 0,
"lectures": [
{
"faculty": "Nancy Wagner",
"day": "Monday",
"topic": "Fluid Mechanics",
"subject": "physics",
"_id": "55cda4dc40f2a8fb0e5cdf8c"
}
]
}
So that works fine, and you don't need to dig to the native methods just to make it work.
Properties of an array make this much easy to query and filter, as well as "aggregate" information across the data, which for all of those MongoDB likes a "strict path" to reference all information. Otherwise you are diffing to only "specific keys", and those cannot be indexed or really searched without mentioning every possible "key combination".
Properties like this are a better way to go. And no bugs here.
The matching element looks like that:
{
"_id": {
"$oid": "519ebd1cef1fce06f90e3157"
},
"from": "Tester2",
"to": "Tester",
"messages": [
{
"username": "Tester2",
"message": "heeey",
"read": false
},
{
"username": "Tester",
"message": "hi!",
"read": false
},
{
"username": "Tester2",
"message": "test",
"read": false
}
],
}
Now I try to set the read property to the current date just of the subelements where the username is not equal to Tester:
var messages = db.collection('messages');
messages.update(
{
_id: new BSON.ObjectID("519ebd1cef1fce06f90e3157"),
messages: {
$elemMatch: { username: { $ne: "Tester" } }
}
},
{ $set: { 'messages.$.read': new Date() } },
{ multi: true }, function(error, result) {
console.log(error);
console.log(result);
});
But just the first messages subelement read property updates:
{
"_id": {
"$oid": "519ebd1cef1fce06f90e3157"
},
"from": "Tester2",
"to": "Tester",
"messages": [
{
"username": "Tester2",
"message": "heeey",
"read": {
"$date": "2013-01-01T00:00:00.000Z"
}
},
{
"username": "Tester",
"message": "hi!",
"read": false
},
{
"username": "Tester2",
"message": "test",
"read": false
}
],
}
What's wrong with the code?
I'm using node.js v0.10.8 and MongoDB v2.4.3 together with node-mongodb-native.
There's nothing wrong with your code; the $ operator contains the index of the first matching array element. The {multi: true} option only makes the update apply to multiple documents, not array elements. To update multiple array elements in a single update you must specify them by numeric index.
So you'd have to do something like this:
messages.update(
{
_id: new BSON.ObjectID("519ebd1cef1fce06f90e3157")
},
{ $set: {
'messages.0.read': new Date(),
'messages.2.read': new Date()
} },
function (err, result) { ... }
);
This is similar to question: How to Update Multiple Array Elements in mongodb
var set = {}, i, l;
for(i=0,l=messages.length;i<l;i++) {
if(messages[i].username != 'tester') {
set['messages.' + i + '.read'] = new Date();
}
}
.update(objId, {$set:set});
I think ArrayFilters can be used in this case
I know it's a little bit late to answer this question but if you use the $[] operator, it will do the trick.
more details here
https://www.mongodb.com/docs/manual/reference/operator/update/positional-all/
I was able to use it inside an updateMany operation, but it should work also for update
var messages = db.collection('messages');
messages.update(
{
_id: new BSON.ObjectID("519ebd1cef1fce06f90e3157"),
messages: {
$elemMatch: { username: { $ne: "Tester" } }
}
},
{ $set: { 'messages.$[].read': new Date() } },
{ multi: true }, function(error, result) {
console.log(error);
console.log(result);
});