Is an ObjectId automatically generated for a nested object? - mongodb

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} ],
// ...
}

Related

Documents inserted without schema not being found with schema

I have two new collections in MongoDB of data that I pulled from an old Firestore database that I'm moving to mongo. Since the total number between these two collections is roughly 20,000, I opted to paste the raw JSON into the insert document section in mongo, which worked like a charm and I didn't have to write a new insert route to do the same.
I then created a schema in Mongoose that matched the inserted documents, and tried to use the schema to pull back some data, and its always returning nothing.
An example of a ticket inserted via JSON:
{
"title": "How to add and manage users for your company in QuickBooks Online",
"priority": "1",
"type": "Video",
"course": "G205",
"transcriptId": "07dom27Zz98jakvB1oh5",
"status": "In Review",
"tags": "",
"url": "",
"revisionNumber": 0,
"directoryId": 19,
"checkedOut": false
},
And my schema I made to match. The collection name in mongo is also called oldTickets, the plural of my schema name here:
const mongoose = require('mongoose');
var Schema = mongoose.Schema
const schema = new Schema({
course: { type: String },
title: { type: String },
priority: { type: String },
type: { type: String },
course: { type: String },
transcriptId: { type: String },
status: { type: String },
tags: { type: String },
url: { type: String },
revisionNumber: { type: Number },
directoryId: { type: Number },
checkedOut: { type: Boolean },
});
module.exports = mongoose.model('oldTicket', schema);
And finally my model import and fetch call:
const OldTicket = require('./models/model_old_ticket');
/***************************************************************************
* Get Old Tickets - Returns all old tickets, 10 at a time
****************************************************************************/
app.get('/getOldTickets/:offset', (req, res) => {
checkConnection();
OldTicket.find().skip(parseInt(req.params.offset)).limit(10).exec((err, data) => {
if (err){ res.status(500).send({err: err}); }
//If we got data, count the tickets & return the tickets & count
if (data) {
OldTicket.find().countDocuments().then(count => {
return res.status(200).send({
tickets: data,
count: count
})
})
}
});
});
Why isn't this finding anything? Both the count and the tickets are 0. I've run into this issue before when manually creating a collection without a schema, and in those instances I would simply delete the collection, write a route to create a document, and then things would work fine. But with the large data size of these two collections, I'd rather not do that since everything should be working as is.
Edit: Example of document in Mongo
And the name of the collection I'm currently viewing:
And I just now realized that for some reason there are now two collection names, oldTickets, which has data, and oldtickets, which is empty. I'm assuming my query is searching through the empty one? How can I get it to go to the one that actually has data?
can you attach the screenshot of your data with the collection? might be it's different.in mongoose, every collection name is complete with 's'. please verify your collection is created manually by you then it has to same as mongoose schema and also completed with 's'.
example:
const mongoose = require("mongoose");
const schema = new mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
index: true
},
filmId: {
type: mongoose.Schema.Types.ObjectId,
index: true
},
filmType: {
type: String,
index: true
},
birthday: {
type: Date
},
age: {
type: Number
},
terms: {
type: Boolean
}
},
{
versionKey: false,
timestamps: true,
}
);
schema.index({ filmId: 1, user: 1 })
module.exports = mongoose.model("UserAgeVerification", schema);
see my database

How to increment a field inside the array of documents in mongo Db?

I want to increment the quantity field inside the products array of the cart when the productId matches to the itemid. Also I want to get the updated document in return.
Here is my Cart Model
import mongoose from 'mongoose';
const cartSchema = new mongoose.Schema({
userId: {
type: String,
required: true,
},
products: [
{
productId: {
type: String,
},
quantity: {
type: Number,
default: 1
}
}
]
}, {timestamps: true});
const Cart = new mongoose.model('Cart', cartSchema);
export default Cart;
and here is what I m doing:
const updatedCart = await Cart.findOneAndUpdate({"products.productId": itemid}, {$inc: {quantity: 1}}, {new: true})
//console.log(updatedCart)
But an empty array return and also quantity didn't increment.
As from your schema, there is no quantity field. Hence your mongoose operation is not updating any document.
Hence it will not return any document as new: true only returns the updated document.
[options.returnOriginal=null] «Boolean» An alias for the new option. returnOriginal: false is equivalent to new: true.
You can use positional $ operator to update the quantity of filtered product in products array.
The positional $ operator identifies an element in an array to update without explicitly specifying the position of the element in the array.
const updatedCart = await Cart.findOneAndUpdate({ "products.productId": itemid },
{ $inc: { "products.$.quantity": 1 } },
{ new: true });
Sample Mongo Playground

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.

Validate array of strings in mongoose

I wanted to validate each value from the request which is array of strings. Something like
emails: [ 'johndoe#gmail.com', 'jandoe#gmail.com' ]
Here is my schema
const UserSchema = mongoose.Schema({
name: {
type: String,
index: true,
required: true,
},
emails: [String],
});
In my validation I wanted to make sure that each email is not already exists in the database. I've tried the following
body("emails").custom((value, { req }) => {
return User.findOne({
emails: { $all: value },
_id: { $ne: req.params.id },
}).then((exists) => {
if (exists) {
return Promise.reject("Email already exists!");
}
});
});
But the problem is if I tried to post multiple emails in array the validation fails and the data will be inserted to db. How can I check if one of the emails already exists and reject the request?
In the docs of $in, it mentioned that:
If the field holds an array, then the $in operator selects the documents whose field holds an array that contains at least one element that matches a value in the specified array...
So you can solve it by:
User.findOne({
emails: { $in: value },
_id: { $ne: req.params.id },
})...

Creating multiple documents in mongoose only if it does not currently exist

I was wondering if to create multiple documents in mongoose, but only if they do not exist currently? From the documentation, I've found the code below to create multiple documents, but just wondering how to ensure that it does not create a document if it currently exist?
In particular, if one document already exists, I would like the other documents that are not currently created to be created (rather than the entire create operation to fail).
From Documentation
var array = [{ type: 'jelly bean' }, { type: 'snickers' }];
Candy.create(array, function (err, jellybean, snickers) {
if (err) // ...
});
As noted in the documentation, the .create() method is a shortcut function for creating a new document for the given model and "saving" it to the collection. This actually works like the more formal .save() method but in shortcut form.
What you are describing though is more akin to the "upsert" behavior of the MongoDB .update() method. Which can also apply to its .findAndModify cousin or specifically in mongoose, the .findOneAndUpdate() method.
So with some sample code:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/nodetest');
var candySchema = new Schema({
type: String
});
var Candy = mongoose.model( "Candy", candySchema );
var array = [
{ type: 'jelly bean' },
{ type: 'snickers' },
{ type: 'mars' },
{ type: 'snickers' }
];
array.forEach(function(n) {
Candy.findOneAndUpdate( n, n, { upsert: true }, function(err,doc) {
console.log( doc );
});
});
You would see the following output:
{ _id: 535088e2e4beaab004e6cd97, type: 'jelly bean' }
{ _id: 535088e2e4beaab004e6cd98, type: 'snickers' }
{ _id: 535088e2e4beaab004e6cd99, type: 'mars' }
{ _id: 535088e2e4beaab004e6cd98, type: 'snickers' }
Noting that the second entry for 'snickers' actually refers to the object that was already created.
So that is a basic way to ensure that you are not actually creating the same data twice as long as you specify the "key" to match in the query condition.