How to create in mongoose if not exists but not update - mongodb

I've only found ways to create OR update a document. I need to create if not exists but do nothing if it already exists. How to do it in mongoose?
Please note that findOneAndUpdate won't work, because if it fins the document, it updates it! I don't want that.
UPDATE:
I just want to create a variable called order_number that can be incremented. However, to increment, I must make sure the document exists. However, I cannot update its value in case it exists.
I tried:
let r = await OrderNumber.findOneAndUpdate({ unique: 0 }, { unique: 0, order_number: 0 }, { upsert: true });
It successfully creates when it does not exist, but always updates order_number to 0. I don't want this to happen.

As only way I've found it via two DB calls as in general inserts doesn't have any filters unless findOneAnd* or upsert on updates are used - which we're not looking at. Then only option is to make two calls, here is basic code, please wrap those in proper function & try/catch to add error handling :
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
const orderNumberSchema = new Schema({
any: {}
}, {
strict: false
});
const OrderNumber = mongoose.model('order_number', orderNumberSchema, 'order_number');
let resp = await OrderNumber.findOne({ unique: 0 });
if (!resp) {
let orderNumberObj = new OrderNumber({ unique: 0, order_number: 0 })
let r = await orderNumberObj.save();
}

Related

Why doesn't my Mongoose Schema unique validation work for the first run?

When I first run my code (no collection in MongoDB), it works. When I run it a second time and a collection already exists, I get an error: duplicate key error collection: exampledb.testusers index: skills.name_1 dup key: { : "CSS" }
const required = true;
const unique = true;
const schema = new Schema({
username: String,
skills: [new Schema({
_id: false,
name: { type: String, required, unique },
level: { type: Number, default: 0 }
})]
});
const User = mongoose.model("user", schema);
const fakes = [];
for (let i = 50; i > 0; i--) {
fakes.push({
username: "CoolZero91",
skills: [
{ name: "JavaScript", level: 9 },
{ name: "CSS", level: 7 }
]
});
}
await User.create(fakes);
Shouldn't I get the same error for the first time as well? What is different when with the unique validation when I don't have an existing collection versus when I DO have an existing collection?
Looking back now, I'm pretty sure this was (or is) an issue with the way uniqueness is checked by indexes.
The Mongoose schema makes an index into the database that enforces the uniqueness. That doesn't (or didn't) seem to happen for the first set of inserts, so the first inserts were not checked for uniqueness by the index! I think this happens if the first insert is batch OR and individual thing - ergo; first-insert batches are not checked for uniqueness.
Thanks #tbhaxor in comments

Mongoose set default on field update if field is not present or null

I have a mongoose schema like this suppose:-
var mSchema = new Schema({
name: { type: String, required: true}
});
and have been using this schema for a year and now i want to add gender to it like this :-
var mSchema = new Schema({
name: { type: String, required: true},
gender: { type: String, default: 'Male' }
});
whenever there will be an update request i want this gender to automatically set Male as default but i found that default don't set on update request.
(Note: It's just an example not a real life scenario. i just want mongoose default work if field is not present or null)
Is there any way in which i can set default on the updation of document ?
If you are using a function like update(), then this is not directly possible as stated by this answer. Still, you can simply switch to a function like findOne() and use save(), which should do the same.
When upserting documents, you can also check out the setDefaultsOnInsert option: https://mongoosejs.com/docs/defaults.html#the-setdefaultsoninsert-option
const options = {
// Create a document if one isn't found. Required
// for `setDefaultsOnInsert`
upsert: true,
setDefaultsOnInsert: true
};
await XY.findOneAndUpdate(query, update, options);

How to update one field from a passed object in mongoose

Incase I have an object that looks like the one below
const auth = {
geohash: args.input.geohash,
offenses: args.input.offenses,
online: args.input.online,
paid: args.input.paid,
profilePic: args.input.profilePic,
username: args.input.username,
}
and I pass it inorder to update a document
const update = { _id: mongoose.Types.ObjectId(args._id) }
const value = await DiscoverUsers.findOneAndUpdate(update, auth, { useFindAndModify: false, new: true })
so incase I only want to update the username and I don't want to keep creating a mutation for updating each field in the document.
lets say my mutation looks like this
mutation{
updateDiscoverUsers(_id:"5dab7c198a83f235c89a964a",input:{username:"peter"}){
username
}
}
but this only updates the username but it makes the rest of the fields null but I only want to find a way to only update the fields I have passed in the mutation and the rest remain the same. so I can update the username and profilePic only and the rest remain unchanged.
I would be grateful for the help and thanks in advance
You should use the atomic operator $set to update only where you want, and you should pass only the fields you want to update, not all of them otherwise all the fields are going to be updated by the new value.
like:
const value = await DiscoverUsers.findOneAndUpdate(update, {$set:{username:"pedro"}}, { useFindAndModify: false, new: true })

How to avoid duplicates while looping through a sorted mongodb cursor

I try to avoid having duplicates returned by a mongodb cursor when write operations occurs while looping through documents.
The following code will loop for ever as we modifiy the updatedAt date while reading the collection.
const mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
const Model = mongoose.model(
'Cat',
mongoose.Schema({ name: String }, { timestamps: true }).index({ updatedAt: 1 })
);
mongoose
.connect('mongodb://localhost/test', { useMongoClient: true })
.then(() => {
const docs = [];
for (var i = 0; i < 2000; i++) {
docs.push({ name: i });
}
return Model.insertMany(docs)
})
.then(() => {
const c = Model.find({}).sort({ updatedAt: 1 }).cursor();
return c.eachAsync((doc) => {
doc.markModified('name');
console.log(doc.name);
return doc.save();
});
});
I tried to use cursor.snapshot() and it works well but it cannot be used on a sorted query.
The only alternative I found is to send a count request first then apply a limit to the cursor without using snapshot at all. This seems to work as mongo seems to stack up modified document FIFO style.
The problem is that document can be inserted between the count and the cursor queries which leads to incomplete data returned by the cursor.
How can I loop through a cursor, using a sorted query without ending up with duplicates documents?

Mongo: find items that don't have a certain field

How to search for documents in a collection that are missing a certain field in MongoDB?
Yeah, it's possible using $exists:
db.things.find( { a : { $exists : false } } ); // return if a is missing
When is true, $exists matches the documents that contain the field, including documents where the field value is null. If is false, the query returns only the documents that do not contain the field.
If you don't care if the field is missing or null (or if it's never null) then you can use the slightly shorter and safer:
db.things.find( { a : null } ); // return if a is missing or null
It's safer because $exists will return true even if the field is null, which often is not the desired result and can lead to an NPE.
just for the reference here, for those of you using mongoose (v6) and trying to use the $exists to find a field that is not defined in your mongoose schema, mongoose v6 will escape it.
see here https://mongoosejs.com/docs/migrating_to_6.html#strictquery-is-removed-and-replaced-by-strict
for example:
const userSchema = new Schema({ name: String });
const User = mongoose.model('User', userSchema);
// By default, this is equivalent to `User.find()` because Mongoose filters out `notInSchema`
await User.find({ notInSchema: 1 });
// Set `strictQuery: false` to opt in to filtering by properties that aren't in the schema
await User.find({ notInSchema: 1 }, null, { strictQuery: false });
// equivalent:
await User.find({ notInSchema: 1 }).setOptions({ strictQuery: false });