Mongoose schema to require array that can be empty - arrays

I have this schema
var StuffSchema = new mongoose.Schema({
_id: { type: String, required: true, unique: true },
name: { type: String, required: true }
});
mongoose.model('Stuff', StuffSchema);
Works fine.
Now I need to add another schema "Cargo" containing this
mystuff: { type:[String], ref: 'Stuff', required:true},
that is, I want mystuff to contain array of ids of Stuff, but this fails with validation error when running this code
mongoose.model('Cargo').create( some data...)
if I use an empty array for the mystuff field.
It seems to work if I change the Cargo schema to
mystuff: { type:[String], ref: 'Stuff'},
but I want the mystuff field to be required and allow empty arrays
What can I do to make this happen?

Empty arrays are created by default (see also this). The attribute required: true requires the array to have at least one element in it (source code). You can remove that attribute to get your desired behavior.
(Aside, mongoose assigns a default _id field with the type ObjectId to all schemas. Declaring it is unnecessary, and using a string is not typical, although certainly allowed.)
Edit Nov 2017: This is a candidate change in Mongoose 5. See https://github.com/Automattic/mongoose/issues/5139.

Related

Mongoose duplicate key error in nested schema

I currently have the following schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Comments = new Schema({
authorId: {
type: Schema.Types.ObjectId,
unique: false
},
commentBody: {
type: String
}
});
//Create schema
const GroupSchema = new Schema({
name:{
type: String,
required: true
},
comments: [Comments]
});
module.exports = Group = mongoose.model('group', GroupSchema);
I can insert the first group with no errors, but as soon as i try to add the second one i get this error:
{
"error": {
"name": "MongoError",
"message": "E11000 duplicate key error collection: app-idiomas.groups index: comments.authorId_1 dup key: { : null }",
"driver": true,
"index": 0,
"code": 11000,
"errmsg": "E11000 duplicate key error collection: app-idiomas.groups index: comments.authorId_1 dup key: { : null }"
}
}
So, as i suppose, the problem is with the "Comments" schema and i have searched about nested schema to see if i can solve this problem. I have tried to use "default" and "sparse" attribute, but no success.
Finally i decided to make a test and remove the Comments schema and it's reference in Groups Schema just to confirm that everything is OK. I erased all my database and added the first group (now without any comments attribute) and i still get the error whenever i try to add the second one. I just don't know why.
Any help ?
UPDATE
I didn't have time in these last days, but finally i found what the problem was. Probably when i was creating the Comments Schema i may have set the authorId to be unique and because of that MongoDB created an index for this attribute. I don't know much about MongoDB and that's why i hadn't checked before. But here is where i found the index and removed it:
Location of "indexes" tab in MongoDB
This problem has to be with record in the database.
But as you said you cleared the database, the problem looks weird.
Just make a check another time whether you deleted the correct database i.e the one you are using in your api.

Meteor inserting into a collection schema with array elements

Hi I created a SimpleSchema for a Mongo collection which has a variable number of sub-documents called measurables. Unfortunately it's been a while since I've done this and I can't remember how to insert into this type of schema! Can someone help me out?
The schema is as follows:
const ExerciseTemplates = new Mongo.Collection('ExerciseTemplates');
const ExerciseTemplateSchema = new SimpleSchema({
name: {
type: String,
label: 'name',
},
description: {
type: String,
label: 'description',
},
createdAt: {
type: Date,
label: 'date',
},
measurables: {
type: Array,
minCount: 1,
},
'measurables.$': Object,
'measurables.$.name': String,
'measurables.$.unit': String,
});
ExerciseTemplates.attachSchema(ExerciseTemplateSchema);
The method is:
Meteor.methods({
addNewExerciseTemplate(name, description, measurables) {
ExerciseTemplates.insert({
name,
description,
createdAt: new Date(),
measurables,
});
},
});
The data sent by my form for measurables is an array of objects.
The SimpleSchema docs seem to be out of date. If I use the example they show with measurables: type: [Object] for an array of objects. I get an error that the the type can't be an array and I should set it to Array.
Any suggestions would be awesome!!
Many thanks in advance!
edit:
The measurable variable contains the following data:
[{name: weight, unit: kg}]
With the schema above I get no error at all, it is silent as if it was successful, but when I check the db via CLI I have no collections. Am I doing something really stupid? When I create a new meteor app, it creates a Mongo db for me I assume - I'm not forgetting to actually create a db or something dumb?
Turns out I was stupid. The schema I posted was correct and works exactly as intended. The problem was that I defined my schema and method in a file in my imports directory, outside both client and server directories. This methods file was imported into the file with the form that calls the method, and therefore available on the client, but not imported into the server.
I guess that the method was being called on the client as a stub so I saw the console.log firing, but the method was not being called on the server therefore not hitting the db.
Good lesson for me regarding the new recommended file structure. Always import server side code in server/main.js!!! :D
Thanks for your help, thought I was going to go mad!

Mongoose indexOf in an ObjectId array

I have the following schema:
var daySchema = new Schema({
date: {
type: Date,
required: true
},
activities: [{
type: Schema.Types.ObjectId,
ref: 'Activity'
}]
});
And I am removing an activity within the daySchema:
var index = day.activities.indexOf("584aa9c16791eb1ec4ad8e73");
day.activities.splice(index, 1);
Can someone explain me why this works ?. The array "activities" is an array of "ObjectId". So the "indexOf" should not work with objects, but still the "indexOf" is able to find the element based on the id. This is driving me crazy, is something else going on here, maybe a map function inside ObjectId ?
This works because Mongoose wraps an array in MongooseArray which provides its own indexOf method which supports this string-based comparison rather than the strict equality test used by the native array implementation.

How to insert an Array of Objects in Meteor WITHOUT autoform?

The example i'm goint o use here is taken from http://autoform.meteor.com/quickform
I've been using autoform for a few projects, but i need to get this same behaviour without it, how could i do an insert of an Array of objects??
So here is the Schema definition you need with autoform
items: {
type: Array,
optional: true,
minCount: 0,
maxCount: 5
},
"items.$": {
type: Object
},
"items.$.name": {
type: String
},
"items.$.quantity": {
type: Number
}
Next is the call to autoform to generete the form in the template
{{> quickForm id="demo" schema=schemaFromJSON type="method" meteormethod="demoSubmission"}}
With that in place you get a form, displaying both fields: name and quantity, plus a sign for ading more objects with those same fields, and when you actually submit the form it inserts all of your objects.
The HTML and CSS is not the problem
I'm not quite sure what you are asking. These are two ways of inserting arrays to collection:
// insert items one by one
// NOTE: Meteor doesn't allow inserting of arrays like `Items.insert(items)`
items.forEach(item => Items.insert(item));
// insert all items simultaneously by using raw mongo collection:
Items.rawCollection().insert(items);

Strategy for sharing and reusing of schema parts in mongoose.js

Suppose, I have two schemas, e.g. for person and company. Both of them should have an address, which consists of a street name, number, zip and city.
What would be the strategy to avoid copying the address properties between the two schema definitions? I read about sub docs, but they seem to be (1) tied to exactly one parent schema, (2) always occur in an array.
Almost too obvious, but this is what I came up with finally:
Define the reusable portions separately, however, contrary to my first thoughts: do not use a Schema here:
var addressSubschema = {
street: String, number: String, zip: String, city: String
}
Simply include this part in actual schemas:
var personSchema = new mongoose.Schema({
name: { type: String, required: true },
title: { type: String },
address: addressSubschema
});
var companySchema = new mongoose.Schema({
name: { type: String, required: true },
addresses: [addressSubschema]
});
My (naive?) approach might be to create a new schema for address, and use Mongoose's population mechanic to link to a given address by ObjectID. However, this is essentially emulating a relational DB behaviour, and I'm not sure how PC that is when using a flat-file store like Mongo.

Resources