Mongoose findOneAndUpdate on model - mongodb

What's the purpose of a Mongoose schema if I'm doing an upsert with findOneAndUpdate call?
Everything I can find seems to indicate if I do a findOneAndUpdate, I need to reference the base schema instead of an instance.
Here's my setup:
const PersonSchema = new mongoose.Schema({
ssn: {
type: Number,
unique: true,
},
first: String,
last: String
})
const Person = mongoose.model("Person", PersonSchema)
const person = new Person({ssn: 123456789, first: "Foo", last: "Bar"})
If I just do a save (and that ssn exists already, I'll get a 'unique' violation).
person.save();
Instead, I'm finding out that I need to do something like
const options = { upsert: true, new: true }
const query = { ssn: 123456789 }
Person.findOneAndUpdate(
query,
{
ssn: 123456789,
first: "Foo",
last: "Bar"
},
options)
OR
const options = { upsert: true, new: true }
const query = { ssn: 123456789 }
const newPerson = Object.assign({}, person._doc)
// delete this so I don't get a conflict with Mongoose on the _id during insert
delete newPerson._id
Person.findOneAndUpdate(query, newPerson, options)
It seems like findOneAndUpdate doesn't care about the specific model (or instance) and is simply just a mechanism to get to the underlying MongoDB method.
Is that the case? Or am I missing something that should be obvious?

Related

MongoDB, collection name appears to be wrong

When I try to call this request
const presidentModel = require('./modules/president.js')
app.get('/president', (req, res) => {
presidentModel.find({}, (err, result) => {
if (err) {
console.log(err)
} {
console.log(result)
}
})
})
It only returns an empty array []
then it creates a new collection with the name 'presidents'
Here is my Schema
const mongoose = require("mongoose")
const presidentSchema = new mongoose.Schema({
nickname: {
type: String,
required: true
},
fullname: {
type: String,
required: true,
},
votes: {
type: Number,
required: true
}
})
const president = mongoose.model("president", presidentSchema)
module.exports = president
The request should return the data on collection "president" but what it does is it creates a new collection with the name "presidents". I don't know where is it coming from tho
it is good practice to have your collections as plural, and therefore mongoose implicitly tries to make collections plural (as there are multiple items to be stored in them).
To override this, you can pass a third parameter to .model() with the name of the collection:
const president = mongoose.model("president", presidentSchema, "president")

How to update document with subdocument, or create new one if none are found

I'm trying to create a new subdocument object in an array when a user calls a command OR update the existing document based on their id. However, everything I've tried either gives me errors or it overrides the existing subdocument with the current user which is not what I want.
enter image description here
Basically I want to add another object in the array ( "1": Object ) that is a second user tracking whether they've used the command or not.
I can't remember all variations on code, but the current code I'm using:
const query = {
_id: guild.id,
members : [{
_id: user.id
}]
}
const update = {
members: {
_id: user.id,
bot: user.bot,
commandUsed: true
}
}
const options = {upsert: true, new: true}
await mongo().then(async () => {
console.log('Updating to...in use')
try {
// Find the document
await commandUsageSchema.findOneAndUpdate(query, update, options, function(error, result) {
if (!error) {
// If the document doesn't exist
if (!result) {
// Create it
result = new userSchema;
}
// Save the document
result.save(function(error) {
if (!error) {
// Do something with the document
} else {
throw error;
}
})
}
})
is creating a duplicate key error which is frustrating. Here is the layout of my schemas:
const mongoose = require('mongoose')
const reqString =
{
type: String,
required: true
}
const userSchema = mongoose.Schema({
_id: reqString,
bot: Boolean,
commandUsed: Boolean
}, {unique: true})
const commandUseSchema = mongoose.Schema({
_id: reqString,
members: [userSchema]
})
module.exports = mongoose.model('command-usage-checker', commandUseSchema)
I'm using mongoose and mongoDB (of course) with javascript and DiscordJS.

How to use setDefaultsOnInsert properly

Am trying to upsert an object with a query below:
await Property.findOneAndUpdate({ "property_id": property_id }, object,{ upsert: true, setDefaultsOnInsert: true })
My schema looks like below:
const mongoose = require('mongoose');
const propertySchema = new mongoose.Schema({
property_id: String,
details: {
location: {
type: {
type: String,
default: "Point"
},
coordinates: [Number],
address: String,
country: {
type: String,
default: 'USA'
},
state: {
type: String,
default: 'CA'
},
city: {
type: String,
default: 'N/A'
},
},
status: {
type: String,
enum: ['pending', 'active', 'deleted', 'suspended'],
default: 'pending'
},
}
},
{
strict: false
});
propertySchema.index({ "details.location.coordinates": "2dsphere" });
mongoose.model('Property', propertySchema);
module.exports = mongoose.model('Property');
Yet, when new objects are inserted, attributes and their default values are not inserted, what am doing wrong ?
There a big difference between MongooseDocument and Object:
on MongooseDocument you could apply any method, like $set or .save() it. So you could modify the DB value directly. (Or convert it to JSON/Object/String and lose this property)
when you are dealing with JSON or vanilla js Object you are modifying the object itself, not the DB value. So if you want to modify the DB document you should find it by object's key and update it.
When you are dealing with Model.method_name, especially with find(One)AndUpdate you should provide object type. NOT MongooseDocument or anything else. In that case you should convert DB doc toObject by this method.
Or, if you receiving DB value via any .find method, you receive MongooseDocument by default, and you should use lean() option right after your find query. If you need js-object for any findAndUpdate method later.
For example:
let mongoose_doc = await model.findByID(id) //returns mongoose doc
but
let js_object = await model.findByID(id).lean() //returns you js object

Is an ObjectId automatically generated for a nested object?

My schema is as follows:
const MessageType = {
// ...
oAuth: { provider: String, id: String },
attachments: [ {name: String, contentType: String} ],
// ...
}
MessageSchema = new mongoose.Schema(MessageType, { timestamps: true});
Messages = mongoose.model("Message", MessageSchema);
When I insert a new Message document using Messages.create, an ObjectId (_id) is also generated for attachments, in addition to my name and contentType fields, ie:
[ { name: "xxx", contentType: "yyy", _id: zzzzzz }]
Why is this happening, for attachments but not oAuth?
For avoiding that the _id was generated you must set the option _id: false, Also if you don't want to save the empty attachments object, you need to set default: undefined.
const MessageTypeSchema = new mongoose.Schema({
oAuth: {
type: String
},
attachments: {
type: [
{
type: String
}
],
_id: false,
default: undefined
}
});
Here the code that I used to test:
console.log('-------- Document with attachments --------');
new MessageTypeModel({
oAuth:'xxxxxxxxxxxxx',
attachments: ['teste.png','teste2.jpg']
}).save().then(result => {
console.log(result);
});
console.log('-------- Document without attachments --------');
new MessageTypeModel({
oAuth:'xxxxxxxxxxxxx'
}).save().then(result => {
console.log(result);
});
And here the result of execution:
Mongoose creates _id for single nested subdocuments or arrays, and your object field oAuth is not one of this cases:
Subdocuments are documents embedded in other documents. In Mongoose,
this means you can nest schemas in other schemas. Mongoose has two
distinct notions of subdocuments: arrays of subdocuments and single
nested subdocuments.
Each subdocument has an _id by default. Mongoose
document arrays have a special id method for searching a document
array to find a document with a given _id.
var childSchema = new Schema({ name: 'string' });
var parentSchema = new Schema({
// Array of subdocuments
children: [childSchema],
// Single nested subdocuments. Caveat: single nested subdocs only work
// in mongoose >= 4.2.0
child: childSchema
});
Link of Mongoose documentation: Mongoose SubDocs
You can define _id : false in attachments array.
const MessageType = {
// ...
attachments: [ {name: String, contentType: String, _id: false} ],
// ...
}

Using mongoose timestamps option does not create properties

I may be missing something obvious, but have read to docs.I had an existing collection. Using Robo3T, I dropped it. In my script, running out of Node, I have defined the schema adding timestamps option as shown below. I run my app. The collection is created. However, there are no timestamps when I view via Robo. Everything else is as I expect. The indices are created. The fields are populated.
I expected two additional properties: createdAt and updatedAt.
I am using mongoose 5.2.7.
const categorySchema = mongoose.Schema(
{
value: String,
recordName: String,
sourceId: Number,
targetId: Number,
requestParameters: Object,
magentoResponse: Object
},
{
autoIndex: true
},
{
timestamps: true
}
);
categorySchema.index({sourceId: 1}, {unique: true});
categorySchema.index({targetId: 1, recordName: 1}, {unique: true});
Oh! I was being an idiot. autoIndex and timestamps should be in the same block. I was being an idiot!
It should have been:
const categorySchema = mongoose.Schema(
{
value: String,
recordName: String,
sourceId: Number,
targetId: Number,
requestParameters: Object,
magentoResponse: Object
},
{
autoIndex: true,
timestamps: true
}
);
categorySchema.index({sourceId: 1}, {unique: true});
categorySchema.index({targetId: 1, recordName: 1}, {unique: true});
How did you re-create those records? If they did not go though mongoose (but via mongoDB client/cli) they would not have those fields. These are mongoose specific.
And are created when you create a new Model and save it:
var thingSchema = new Schema({..}, { timestamps: { createdAt: 'created_at' } });
var Thing = mongoose.model('Thing', thingSchema);
var thing = new Thing();
thing.save(); // `created_at` & `updatedAt` will be included