MongoDB updateOne returning null for upsertedId - node.js

I'm trying to get the upsertedId for the updated doucment, but it's returning null.
Here's the function for updating a doucment and trying to return result.upsertedId:
async function run() {
try {
await client.connect()
const collection = client.db("database").collection("collection")
const filter = { username: `${username}`}
const options = { upsert: true };
const updateDoc = {
$set: { date: new Date(), topartists: data.body.items, toptracks: toptracks }
};
await collection.updateOne(filter, updateDoc, options)
.then(result => {
console.log(`${result.matchedCount} document(s) matched the filter, updated ${result.modifiedCount} document(s)`)
console.log(result.upsertedId)
})
console.log('done1')
} finally {
await client.close();
}
}
and here's the console output of the function:
1 document(s) matched the filter, updated 1 document(s)
null
done1
I'm just starting with MongoDB, and the end goal here is to get
_id. It works there is nothing in my database (when it has to make a new document instead of updating one).

That will not be the case.
Your log clearly says - your query matched 1 document and it updated one document.
If your update results in insert, then response of your update operation will have upsert ids. - Means, matched count is 0.

Related

Returning the documents that were upserted from a BulkWrite operation

I am trying to return the documents that were upserted from a bulkwrite operation but it doesn't work. So here is the code in JS:
import Product from '../models/product.model';
import { values } from 'lodash';
let results = []; // this is an array of records to be upserted into the DB, it is not empty
let result = {}
Product.bulkWrite(
results.map((aproduct) =>
({
updateOne: {
filter: {id : aproduct.id},
update: {$set: aproduct},
upsert: true
}
})
)
).then(results => {
// Trying to fetch the documents that have been upserted by the BulkWrite operation
Product.find({'_id': { $in: values(results.upsertedIds)}}).then(function(docs) {
console.info("UPSERTED DOCS: ", docs);
result.upsertedDocuments = docs;
console.info("Merged Results: ", result);
arrNewIds = docs;
});
}
}
// The issue is that from this line, the result object is empty despite being populated in the then as shown above.
// The expected result would have been
// result.upsertedDocuments to hold the upserted documents but it doesn't seem to be case despite the very same variable being populated in the then function

finding multiple documents with mongoose

this is what happens when I console.log()
this is what happens when I return all documents with that id, I just get the first document
const followingUsers = await User.find({ _id: { $in: foundUser.followings } })
const getFeedData = async() => {
for (let user of followingUsers) {
for (let postId of user.posts) {
console.log(postId)
}
}
}
I'm running this code when I console.log(postId) it returns all the posts with that id, but when I try to retrieve all documents with that id it returns just one document
findById will only return one record or null, an ID is the _id field on each document in a collection, which is a unique value
find is the equivalent of a where command in SQL, it returns as many documents that match the query, or an empty array
passing $in as a query to find looks for an array of matching document for the user id's
so if you already know the document _id's, then find will return the ID's so long you have passed an array of valid ObjectId
// (pretending these are real id's)
const arrayOfUserIds = [
ObjectId("5af619de653438ba9c91b291"),
ObjectId("5af619de653438ba9c91b293"),
ObjectId("5af619de653438ba9c91b297")
]
const users = await User.find({ _id: { $in: arrayOfUserIds } })
console.log(users.length)
users.forEach((user, index) => {
console.log(`${index} - `, user._id)
})
// => 3
// => 0 - ObjectId("5af619de653438ba9c91b291")
// => 1 - ObjectId("5af619de653438ba9c91b293")
// => 2 - ObjectId("5af619de653438ba9c91b297")

How to update many documents on mongo and return that updated document?

How can I update many documents on mongoose and return those updated documents so I then I can pass the updated docs to a different service on my code? This is seems like something simple to do but I'm getting confused on my head on how to implement it
In my current code I just update the documents on batch with updateMany, but as the mongo documentation says the writeConcern returned is just the # of docs updated {n: 0 } not the actual documents.
Current Code:
const checkAndUpdateSubscription = () => {
const filter = {
"payments.0.stripeSubscriptionEndDate": { $lte: today },
hasPaid: true,
};
const update = { $set: { hasPaid: false, isSubscriptionNew: 0 } };
const options = { new: true, useFindAndModify: false };
return new Promise((resolve, reject) => {
ModelModel.updateMany(filter, update, options)
.then((response) => {
console.log('response inside checkAndUpdateSubscription', response)
resolve(response);
})
.catch((error) => {
reject(error);
});
});
};
I would like to change it to something similar to my pseudo code below.
What I would like to do:
const checkAndUpdateSubscription = () => {
const filter = {
"payments.0.stripeSubscriptionEndDate": { $lte: today },
hasPaid: true,
};
const update = { $set: { hasPaid: false, isSubscriptionNew: 0 } };
const options = { new: true, useFindAndModify: false };
return new Promise((resolve, reject) => {
// 1. ModelModel.find where stripeSubscriptionEndDate $lte than today ..etc
// 2. Update the document(s)
// 3. Return the updated document(s)
(//4.) .then(updatedModel => sendUpdateModelToOutsideService(updatedModel))
});
};
I don't know if this is necessary in the context of this question but the checkAndUpdateSubscription method is a function that runs every 1min in my db for all my users (# ~thousands)
You can do those as alternative solutions
(maybe there is simpler way, but those will work i think)
Find the ids
find to get those ids
ids = find(your_filter,project_keep_id_only), and then make ids=[_id1, _id2 ...] an array of the ids
update with filter _id $in ids
update({"_id" : {"$in" : ids}},your_update_set_etc)
find to get the updated documents
docs=find({"_id" : {"$in" : ids}})
*if ids are not too many it will be fine i think
Mark the updated with extra field
on update set one extra field with the update_id
after the update, do a find based on that update_id, to get the documents
and if you want your remove that extra field after
If this run parallel, this extra field could be an array, with many update_ids, that you remove them after you get those documents back.

Mongoose updateOne with parameter {new:true} not showing actual updated value

I am struggling for a couple of hours to show the final value of an updated document (via mongoose updateOne). I successfully modify it as I can see "nModified: 1" when I call the endpoint on Postman, but I am not able to output the actual final document - even when using the parameter {new:true}
This is the code for the route:
// 3. We check if blockid is in this project
Block.findById(req.params.blockid)
.then(block => {
if (!block) {
errors.noblock = "Block not found";
return res.status(404).json(errors);
}
// 4. We found the block, so we modify it
Block.updateOne(
{ _id: req.params.blockid },
{ $set: blockFields }, // data to be updated
{ new: true }, // flag to show the new updated document
(err, block) => {
if (err) {
errors.noblock = "Block not found";
return res.status(404).json(errors);
}
console.log(block);
res.json(block);
}
);
})
.catch(err => console.error(err));
Instead, this is the output I am getting (Mongoose is on debug mode)
Any ideas?
Many thanks
{ new : true } will return the modified document rather than the original. updateOne doesn't have this option. If you need response as updated document use findOneAndUpdate.
Below are the mongoosejs function where you can use { new : true }
findByIdAndUpdate()
findOneAndUpdate()
findOneAndDelete()
findOneAndRemove()
findOneAndReplace()
Thank you #sivasankar for the answer. Here is the updated working version with findOneAndUpdate
And here the expected result:
you should give second param as object of keys value paris of data,
don't pass as $Set : blockfields, just add like below, if it is object containing parameters,
{ $set: blockFields }
Because code should be like this
Block.updateOne(
{ _id: req.params.blockid },
blockFields, // if blockfields is object containing parameters
{ new: true },
(err, block) => {
// lines of code
}
);
For more detail here is link to updateOne function detail updateOne

Mongoose not persisting returned object

Mongo: 3.2.1.
I have a model defined as such:
var MySchema = new Schema(
{
....
records: {type: Array, "default": []};
I first create an object based on that schema with no record field and it's correctly added to the database. I then update that object as such:
Client
angular.extend(this.object.records, [{test: 'test'}]);
this.Service.update(this.object);
Server (omitting the none-problematic code)
function saveUpdates(updates) {
return function(entity) {
var updated = _.merge(entity, updates);
return updated.save()
.then(updated => {
console.log(updated);
Model.find({_id: updated._id}).then((data)=> console.log(data));
return updated;
});
};
}
The first console.log prints the object with records field updated. The second prints the object without. What am I missing? How can the resolved promise be different than the persisted object? Shouldn't data and updated be identical?
I think you have a couple problems.
You are using the variable 'updated' twice.
var updated = _.merge(entity, updates); // declared here
return updated.save()
.then(updated => { // trying to re-declare here
The other issue might be that you are trying to merge the 'updates' property with the mongo object and not the actual object values. Try calling .toObject() on your mongo object to get the data.
function saveUpdates(updates) {
return function(entity) {
// call .toObject() to get the data values
var entityObject = entity.toObject();
// now merge updates values with the data values
var updated = _.merge(entityObject, updates);
// use findByIdAndUpdate to update
// I added runValidators in case you have any validation you need
return Model
.findByIdAndUpdate(entity._id, updated, {
runValidators: true
})
.exec()
.then(updatedEntity => {
console.log(updatedEntity);
Model.find({_id: entity._id})
.then(data => console.log(data));
});
}
}

Resources