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

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.

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

Cannot push more than one documents to collection (11000)

This is my Operation Model:
const mongoose = require('mongoose');
const operationSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
extract: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Extract'
},
normalizations: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Normalize'
}],
ingestions: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Ingest'
}]
});
module.exports = mongoose.model('Operation', operationSchema);
This is how I am creating an Operation:
exports.create = async (req, res, nexr) => {
let op = new Operation
op.user = mongoose.Types.ObjectId(req.body.user);
op.extract = mongoose.Types.ObjectId(req.body.extract);
op.normalizations = [];
op.ingestions = [];
const operation = new Operation(op);
try {
await operation.save();
console.log(operation)
res.send(operation);
} catch (err) {
console.log(operation)
res.status(500).send(err);
}
}
NO UNIQUE LABELS exists in any of the User, Extract, Normalize, Ingest Models
When I try to push to Mongo, the first time everything is ok.
If I try to push more than one records I get this error from Postman:
{
"driver": true,
"name": "MongoError",
"index": 0,
"code": 11000,
"keyPattern": {
"extract.name": 1
},
"keyValue": {
"extract.name": null
} }
only a single document in the collection can be missing the unique field.There is already a document in this collection with a missing or null value. The index is not present in the collection in which you are trying to insert. So try this, drop that collection from the database and run the program again.
Any documents missing the source_references.key field will be considered as having a null value. If you want the index to only apply to documents with a source_references.key field you can use sparse:true index creation option.

Update model, delete its array and populate new array using MongoDB Mongoose

I wish to ask for advice on the best way to do the following performance-wise.
so, for example, I have a model
person :{
_id: "12312",
name: "hello",
phones: [
{ number : "123456" }
]
}
when I update the person model, like the name: "newname", I also wish to clear the phone array and populate it with a new one.
i was thinking of doing a findById/findOne().deleteArray().populateArray() chaining.
what would be the best path to chain it in Mongoose?
I figure deleting the array is the quickest since the id is a random alphanumeric and not some incrementing long value so might as well delete everything and recreate the array
thoughts?
This is my function in Express/Mongoose
exports.update = function(req, res) {
Person.findByIdAndUpdate(req.params.person_id, person, { new: true, runValidators: true }, function(err, person) {
if (err) res.send(err);
res.json(person);
});
};
You can set the phones array with an empty array with findOneAndUpdate like this:
const newDoc = {
name: "newname",
phones: []
}
let doc = await Person.findOneAndUpdate({ name: newDoc.name }, newDoc, {
new: true
});
console.log(doc);
or if you want to replace existing phones with new ones you can use an object like this:
const newDoc = {
name: "newname",
phones: ["1","2"]
}

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

Mongoose populate array

I can't get mongoose to populate an array of objects.
The schema is as follows:
var topOrganisationsForCategorySchema = new mongoose.Schema({
category: String,
topOrganisations: [{
organisation: {
type: mongoose.Schema.Types.ObjectId,
ref: 'organisation'
},
model: mongoose.Schema.Types.Mixed
}]
});
module.exports = mongoose.model('topOrganisationsForCategory', topOrganisationsForCategorySchema);
I would like all of the objects in this collection populated with an array of organisations.
Here is what i have tried
TopOrganisationsForCategory
.find()
.exec(function(err, organisation) {
var options = {
path: 'topOrganisations.organisation',
model: 'organisation'
};
if (err) return res.json(500);
Organisation.populate(organisation, options, function(err, org) {
res.json(org);
});
});
var organisationSchema = new mongoose.Schema({
name: String,
aliases: [String],
categories: [String],
id: {
type: String,
unique: true
},
idType: String
});
organisationSchema.index({
name: 'text'
});
module.exports = mongoose.model('organisation', organisationSchema);
You're close but a couple notes:
The following code assumes you also have a schema/model declaration for Oranisation.
I am not sure if the model property is meant as an option (which would be invalid) or actually is a property of topOrganisations.
So, I left model in as it shouldn't cause any issues but be aware that if you were using it as an option it is not doing what you might think it is.
// Assuming this schema exists
var organisationSchema = new mongoose.Schema({...});
var topOrganisationsForCategorySchema = new mongoose.Schema({
category: String,
topOrganisations: [{
organisation: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Organisation' // Model name convention is to begin with a capital letter
}
// Is `model` supposed to be for the ref above? If so, that is declared in the
// Organisation model
model: mongoose.Schema.Types.Mixed
}]
});
// Assuming these model definitions exist
var Organisation = mongoose.model('Organisation', organisationSchema);
var TopOrganisationsForCategory = mongoose.model('TopOrganisationsForCategory', TopOrganisationsForCategorySchema);
// Assuming there are documents in the organisations collection
TopOrganisationsForCategory
.find()
// Because the `ref` is specified in the schema, Mongoose knows which
// collection to use to perform the population
.populate('topOrganisations.organisation')
.exec(function(err, orgs) {
if (err) {
return res.json(500);
}
res.json(orgs);
});