insering a mongoose record if not found - mongodb

The mongoose schema looks like:
var followSchema = new Schema({
userId: {type: String, required: true},
following : []
});
The Objective is to look for a user using the ID field and then add items to the following array.
The below method does not work:
var myRecord = FollowModel.findOneAndUpdate({userId: req.user.Id}, {upsert: true});
myRecord.exec(function(err, result) {
result.following.push('Test');
res.status(200).send('New Item Added to Follow List');
});
How is this to be done ?

It does work, you just forgot to include the { "new": true } option, as well as a valid update statment:
FollowModel.findOneAndUpdate(
{ "userId": req.user.Id},
{ "$setOnInsert": {
}},
{ "upsert": true, "new": true },
function(err,result) {
if (err) throw err;
console.log(result);
}
);
The blank $setOnInsert here will no nothing in this case, but otherwise you would put something in there that you wanted created "on insert". This would be applied along with any fields in the "query" portion of the statement.
Other standard update operators can also apply, but generally in the case where something "is" matched that you want to update.
But it's the { "new": true } here that lets the command know to return the "modified or created" document, rather than the "original or non existing" document, which is the default without that option.

Related

Issue using $pull from nested Mongoose array atomic operation

I can't seem to get this mongoose $pull operation to work at all. I have tried many solutions, but would ultimately like to use an atomic operation to achieve this as that is the design pattern in the rest of my code.
In my List schema, I have an array of Item objects. Each Item object has an array of Notes which are all string values. Creating, Reading, and Updating these Notes arrays has been no issue. But I can't seem to get the Delete functionality to work.
Schemas:
List
const listSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "Users"
},
name: {
type: String
},
items: {
type: Array,
default: [itemSchema]
}
});
Item
const itemsSchema = {
item: String,
style: String,
moved: Boolean,
notes: Array
};
Sample Document:
_id: 6186940ce10fbd7cec9fb01f
items: Array
0: Object
notes: Array
0: "Updated again x2"
1: "Test one to delete edit"
2: "New one"
_id: 6186d98dcef2ae43605becc4
item: "Test 1"
style: ""
moved: false
1: Object
notes: Array
_id: 6186d98fcef2ae43605becc5
item: "Test 2"
style: ""
moved: false
2: Object
notes: Array
_id: 6186d991cef2ae43605becc6
item: "Test 3"
style: ""
moved: false
3: Object
notes: Array
0: "Add from none"
1: "typing a really long sentence here to see what happens when I get to t..."
2: "Test"
_id: 6186d994cef2ae43605becc7
item: "Test 4"
style: ""
moved: false
user: 611083d8baed4458d8dcd273
name: "Nov-06-2021"
__v: 0
Methods:
Create
List.findOneAndUpdate(
{ "user": req.user.id, "name": list, "items._id": ObjectId(id) },
{ "$push": { "items.$.notes": newNote } },
{ new: true },
(err, newList) => {}
)
Update
List.findOneAndUpdate(
{ "user": req.user.id, "name": list, "items._id": ObjectId(id) },
{ "$set": { "items.$.notes.$[note]": newNoteText } },
{
arrayFilters: [{"note": note}],
new: true
},
(err, newList) => {}
)
Delete (not working)
List.findOneAndUpdate(
{ "user": req.user.id, "name": list, "items._id": ObjectId(id) },
{ "$pull": { "items.$.notes.$[note]": note } },
{
arrayFilters: [{"note": note}],
new: true
},
(err, newList) => {}
)
The current Delete code block is close to what I'd like my solution to look like. By all rights, and everything I've read, it should already work. Can anyone show me what will work, and possibly why this isn't currently working?
I have tried many solutions including using $in, $[], $elemMatch, and a bunch more outside the box solutions. I don't see why $push and $set work without issue, but $pull is deciding to do nothing.
The current response I get from MongoDb with this operation doesn't include an error message, but the returned newList is a null value, and no change is reflected in my DB. I am currently on the latest version of mongoose: 5.13.13.
The solution to this was to remove the $[note]from my operator as $pull requires an array to operate on, not the array object.
I definitely tried that before, before there was an issue with my request body that was coming from the front end. I wasn't using the correct syntax for an axios.delete, however, and that was my main issue.
Solution
List.findOneAndUpdate(
{ "user": req.user.id, "name": list, "items._id": ObjectId(id) },
{ "$pull": { "items.$.notes": note } },
{ new: true },
(err, newList) => {}
)
I removed $[note] as a variable because $pull operates on the entire notes array. Therefore, I no longer need the arrayFilters because it is already looking for the note variable.

mongodb find and update one with multiple conditions

{
roomId: "id",
questions:{
q1:{
user1:"user1's work"
}
}
}
I'm trying to query mongodb with multiple conditions, that roomId has to match, and questions must be q1, and in q1 there must be a user1.
Here's what I've tried so far. Using and operator, but doesn't seems to work.For now I'm using find, as I read in the docs that updateMany has the same query selector as find.
const result = await collection.find({
$and: [
{
roomId: roomId,
},
{
questions: {
currentQuestion: {
userName,
},
},
},
],
});
My schema:
{
roomId: "id",
roomName:"roomName",
questions:{
question1:{
user1:"user1's work",
userN: "userN's work"
},
questionN:{
user1:"",
userN:""
}
}
}
My expected input , (roomId, currentQuestion, userName) for query conditions,"userWork" to be inserted to what's under userName (user1 - userN).
Expected output, that the user's work gets updated with "userWork", under the right room, right currentQuestion and the right user.
You need this query I think:
db.collection.find({
"roomId": "id",
"questions.q1.user1": {
$exists: true
}
})
This query find a roomId which match your 'id' and then, check if exists the element questions.q1.user1.
Mongo playground example here
PS: You said update but... what do you want to update?
Assuming your schema is like
{
roomId: "id",
questions: {
q1: {
user1: "user1's work",
currentQuestion: "currentQuestion1"
}
}
}
Then, the query to update the currentQuestion field whose match the id and has existing questions.q1.user1 is this:
db.collection.update({
"roomId": "id",
"questions.q1.user1": {
$exists: true
}
},
{
"$set": {
"questions.q1.currentQuestion": "new question"
}
})
Example here
Note that if currentQuestion is one level up, you only have to modify $set object.
If you are not asking for this, please provide a complete schema, example input and expected output.

Mongoose findOneAndUpdate updates the wrong item

In my model I have a thread that contains an array of comments. When I try to update these comments it always updates the wrong one and I cannot figure out why.
Thread.findOneAndUpdate({
'comments._id': req.params.commentId,
'comments.created_by.user_id': user._id
},
{
$set: {
'comments.$.content': req.body.content,
}
}, { new: true }, (err, thread) => {
if(!err && thread){
res.status(200).json({success: true})
}
})
This code works, but it always updates the first comment rather than the one I am trying to update.

How to save / update multiple documents in mongoose

I am reading all documents of a specific schema from Mongoose. Now in my program I am doing some modifications to the results I got from Mongoose over time. Something like this:
var model = mongoose.model("Doc", docSchema);
model.find(function(err, result){
// for each result do some modifications
});
How can I send all the results back to the database to be saved? Currently I am iterating the documents and doing a save() on every document. I think there must be a better way. But currently I only find information on updating documents IN the database without returning them. Or bulk updates which do the SAME to update to each document.
You can use update query with multi:true which update all documents in your db.
please find below reference code,
model.update({ "_id": id }, { $set: { "Key": "Value" } }, { multi: true }, function (err, records) {
if (err || !records) {
return res.json({ status: 500, message: "Unable to update documents." });
} else {
return res.json({ status: 200, message: "success" });
}
});
If you are trying to make the same change to each document in the results, you could do something like this:
model.update({ _id: { $in: results.map(doc=>doc._id) }}, { yourField: 'new value' }, { multi: true })

How can I rename a field for all documents in MongoDB?

Assuming I have a collection in MongoDB with 5000 records, each containing something similar to:
{
"occupation":"Doctor",
"name": {
"first":"Jimmy",
"additional":"Smith"
}
Is there an easy way to rename the field "additional" to "last" in all documents? I saw the $rename operator in the documentation but I'm not really clear on how to specify a subfield.
You can use:
db.foo.update({}, {
$rename: {
"name.additional": "name.last"
}
}, false, true);
Or to just update the docs which contain the property:
db.foo.update({
"name.additional": {
$exists: true
}
}, {
$rename: {
"name.additional": "name.last"
}
}, false, true);
The false, true in the method above are: { upsert:false, multi:true }. You need the multi:true to update all your records.
Or you can use the former way:
remap = function (x) {
if (x.additional) {
db.foo.update({
_id: x._id
}, {
$set: {
"name.last": x.name.additional
}, $unset: {
"name.additional": 1
}
});
}
}
db.foo.find().forEach(remap);
In MongoDB 3.2 you can also use
db.students.updateMany({}, {
$rename: {
"oldname": "newname"
}
})
The general syntax of this is
db.collection.updateMany(filter, update, options)
https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/
You can use the $rename field update operator:
db.collection.update(
{},
{ $rename: { 'name.additional': 'name.last' } },
{ multi: true }
)
If ever you need to do the same thing with mongoid:
Model.all.rename(:old_field, :new_field)
UPDATE
There is change in the syntax in monogoid 4.0.0:
Model.all.rename(old_field: :new_field)
Anyone could potentially use this command to rename a field from the collection (By not using any _id):
dbName.collectionName.update({}, {$rename:{"oldFieldName":"newFieldName"}}, false, true);
see FYI
I am using ,Mongo 3.4.0
The $rename operator updates the name of a field and has the following form:
{$rename: { <field1>: <newName1>, <field2>: <newName2>, ... } }
for e.g
db.getCollection('user').update( { _id: 1 }, { $rename: { 'fname': 'FirstName', 'lname': 'LastName' } } )
The new field name must differ from the existing field name. To specify a in an embedded document, use dot notation.
This operation renames the field nmae to name for all documents in the collection:
db.getCollection('user').updateMany( {}, { $rename: { "add": "Address" } } )
db.getCollection('user').update({}, {$rename:{"name.first":"name.FirstName"}}, false, true);
In the method above false, true are: { upsert:false, multi:true }.To update all your records, You need the multi:true.
Rename a Field in an Embedded Document
db.getCollection('user').update( { _id: 1 }, { $rename: { "name.first": "name.fname" } } )
use link : https://docs.mongodb.com/manual/reference/operator/update/rename/
This nodejs code just do that , as #Felix Yan mentioned former way seems to work just fine , i had some issues with other snipets hope this helps.
This will rename column "oldColumnName" to be "newColumnName" of table "documents"
var MongoClient = require('mongodb').MongoClient
, assert = require('assert');
// Connection URL
//var url = 'mongodb://localhost:27017/myproject';
var url = 'mongodb://myuser:mypwd#myserver.cloud.com:portNumber/databasename';
// Use connect method to connect to the server
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
console.log("Connected successfully to server");
renameDBColumn(db, function() {
db.close();
});
});
//
// This function should be used for renaming a field for all documents
//
var renameDBColumn = function(db, callback) {
// Get the documents collection
console.log("renaming database column of table documents");
//use the former way:
remap = function (x) {
if (x.oldColumnName){
db.collection('documents').update({_id:x._id}, {$set:{"newColumnName":x.oldColumnName}, $unset:{"oldColumnName":1}});
}
}
db.collection('documents').find().forEach(remap);
console.log("db table documents remap successfully!");
}
If you are using MongoMapper, this works:
Access.collection.update( {}, { '$rename' => { 'location' => 'location_info' } }, :multi => true )