Say that my Schema is:
var fooSchema = new Schema({
foo: String
});
and I want to add select: false to foo. How would I do that? Without doing the following:
var fooSchema = new Schema({
foo: { type: String, select: false }
});
Can I do fooSchema.<somethingToAddSelectFalseToFoo>?
I assume that this would work: Mongoose schema.set() function: http://mongoosejs.com/docs/api.html#schema_Schema-set.
Schema#set(key, [value])
Sets/gets a schema option.
Parameters:
key <String> option name
[value] <Object> if not passed, the current option value is returned
fooSchema.set('foo', { select: false });
In a select query you could exclude it, i.e. fooSchema.find().select("-foo"). Some examples are at https://stackoverflow.com/a/12097874/442472.
Related
I have schema type Map in my mongoose model. In this map, each element has reference to another model. I know that it's possible to populate attributes in array, but how about Map type? Be cause nesting like "map_type_attribute.some_attribute_to_populate" doesn't work. :)
This is my model:
const Mongoose = require('mongoose');
const parameter = Mongoose.Schema({
definition: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'Definition',
},
value: {},
}, {_id: false});
const schema = Mongoose.Schema({
model: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'Model'
},
name: String,
objectid: Number,
externalId: String,
properties: Mongoose.Schema.Types.Mixed,
parameters: {
type: Map,
of: parameter
}
});
module.exports = Mongoose.model('Element', schema);
This is how i'm trying to populate definition field:
const request = Element.find(query, projection);
request.populate('parameters.definition');
request.exec( (err, docs) => {
...
This functionality was added in Mongoose 5.10.3 (September 2020). You simply denote every element in the map with a $*.
In your example this would be:
const request = Element.find(query, projection);
request.populate('parameters.$*.definition');
request.exec( (err, docs) => {
I also trying to find answer on this question. It seems that deep-populate work, but only if you put keys from the Map to populate method/function. In your case, if you have data like:
{
model: ObjectId("111"),
name: "MyName",
objectid: 111,
externalId: "ExternalId",
properties: ...,
parameters:{
"parameter1":{
"definition":ObjectId("333"),
"value":"value of parameter 1"
},
"parameter2":{
"definition": ObjectId("444"),
"value": "value of parameter 2"
}
}
}
Then you may find and populate like this:
Element.find({}).populate("parameters.parameter1.definiton")
But it's not a good solution. It would be nice if we have something like regexp inside this populate path.
Currently I've managed only to grab all inner collection, and then manually work with Map to substitude collections.
It shouldn't be a huge overhead, since you have only 2 queries to DB. In you case it can be like:
const elements = Element.find({});
const parameters = Parameter.find({});
// go through the elements.parameters and replace it with appropriate value from parameters collection.
I would like to implement some validation logic within a child document, that validation is related to the state of a field of its parent. My code looks like:
const childSchema = function (parent) {
return new Schema({
name: {
type: String,
set: function (v) {
const prefix = (parent.isFlashed) ? 'with' : 'without'
return `${prefix} ${v}`
}
},
version: String
})
}
const parentSchema = new Schema({
isFlashed: Boolean,
firmware: childSchema(this)
})
I'm wondering why my code doesn't work and how can I check the value of a property of the parent schema inside my child schema.
Thanks in advance
You don't need to define your child schema as a function that returns a new Schema. You just reference the child schema in the parent.
const ChildSchema = new Schema({
name: {
type: String,
set: function(v) {
const prefix = this.parent().isFlashed ? 'with' : 'without';
return `${prefix} ${v}`;
}
}
});
const ParentSchema = new Schema({
isFlashed: Boolean,
firmware: ChildSchema
});
You'll notice that I reference the parent object as a function of this: this.parent().
When you're creating a new parent object with a child, you just use a nested object that matches the format of the child schema.
const Parent = mongoose.model('Parent', ParentSchema);
const parent = new Parent({isFlashed: false, firmware: {name: "ChildName"}});
Let's say we have a Mongoose schema in our Node.js project:
let coolSchema = new mongoose.Schema({
field_1 : Number,
field_2 : String,
field_3 : [ String ],
});
And let's we have an according object:
var data = {
field_1 : 123,
field_2 : 'blah',
field_3 : ['aa', 'bb'],
};
Now to save this data into MongoDB we can use this code:
let Model = require('mongoose').model('CoolModel', coolSchema);
(new Model(data)).save();
Ok, while it's all cool.
But if data does not contain field_3 (array field, and the same will be for an object field) Mongoose will anyway add this field into the being created document with empty value.
Can we somehow tell Mongoose not to create this field if it's not contained in the data object?
you can do it easily skip the array field and array of object field.. This will let you skip saving empty array in new documents.but you have to use pre hook for this .
var brandSchema = new Schema({
name : {type:String},
email:String,
check:[]
})
brandSchema.pre('save', function (next) {
if (this.isNew && 0 === this.check.length) {
this.check = undefined;
}
next();
})
when new document is inserted in your schema you have to use this middlware.this works fine so try this.
this is the response when we want to insert any document
"data": {
"__v": 0,
"name": "testing",
"email": "testing#gmail.com",
"_id": "5915b018e292833edda8837f"
}
so i have send only email and name but check(array) field is skipped(Not send any value).
The accepted answer is good. But if you wouldn't want to use pre-hook, then you can add default: undefined to the array fields. For example:
var schema = new Schema({
myArr: { type: [String], default: undefined }
});
Refer to this comment for more explanation.
Not particularly an answer to the question itself but some thought on the matter.
It's not clear exactly what you're trying to achieve here. You defined a schema that is supposed to contain a list of string. Mongoose correctly does so that the data saved in your schema is consistent with the definition of the schema.
In this case, the list is more of a structural part of the schema. If you have different behaviour, you'd have to handle special case in your code in case the list isn't present. Now, you can safely assume that you schema is always returning a list so fetching some data will always allow you to do:
coolData.field_3.forEach(function(x) {
do_cool_things(x)
})
What you're asking is to make the schema allow inconsistent data being returned from mongodb... In other words, you'd have to do this in order to prevent accessing attributes on undefined:
if (coolData.field_3) {
coolData.field_3.forEach(function(x) {
do_cool_things(x)
})
}
Also, I you were trying to optimize the size of you objects/database, you could fill a bug report so mongoose doesn't define empty values while saving the objects and autofill them with defaults when the field is missing from mongodb. (I could be wrong but did you actually check if the data in mongodb was containing empty values or you were just looking at data coming from mongoose?)
It's because you're not marking the fields as required in your schema definition.
Do this:
let coolSchema = new mongoose.Schema({
field_1 : { type: Number, required: true },
field_2 : { type: String, required: true },
field_3 : { type: [ String ], required: true },
});
I have these mongoose schemas:
var ItemSchema = new Schema({
"pieces": Number,
"item": { type: Schema.Types.ObjectId, ref: 'Items' }
});
var cartSchema= new Schema({
"items": [ItemSchema]
});
but when I want to push a new item in items, mongoose add an _id field(on the new item) but I don't understand why.
if you want to add item without _id field then you should add { _id: false } in ItemSchema.
var ItemSchema = new Schema({
"pieces": Number,
"item": { type: Schema.Types.ObjectId, ref: 'Items' }
}, { _id: false });
Mongoose assigns each of your schemas an _id field by default if one is not passed into the Schema constructor. The type assigned is an ObjectId to coincide with MongoDB's default behavior. If you don't want an _id added to your schema at all, you may disable it using this option.
You can only use this option on sub-documents. Mongoose can't save a document without knowing its id, so you will get an error if you try to save a document without an _id.
Link: http://mongoosejs.com/docs/guide.html#_id
How can I add a schema to another schema? This doesn't seem to be valid:
var UserSchema = new Schema({
name : String,
app_key : String,
app_secret : String
})
var TaskSchema = new Schema({
name : String,
lastPerformed : Date,
folder : String,
user : UserSchema
})
I checked the website and it shows how to declare it for an array but not for single.
Thanks
There are a few ways to do this. The simplest is just this:
var TaskSchema = new Schema({
name : String,
lastPerformed : Date,
folder : String,
user : Schema.ObjectId
});
Then you just have to make sure your app is writing that id and using it in queries to fetch "related" data as necessary.
This is fine when searching tasks by user id, but more cumbersome when querying the user by task id:
// Get tasks with user id
Task.find({user: user_id}, function(err, tasks) {...});
// Get user from task id
Task.findById(id, function(err, task) {
User.findById(task.user, function(err, user) {
// do stuff with user
}
}
Another way is to take advantage of Mongoose's populate feature to simplify your queries. To get this, you could do the following:
var UserSchema = new Schema({
name : String,
app_key : String,
app_secret : String,
tasks : [{type: Schema.ObjectId, ref: 'Task'}] // assuming you name your model Task
});
var TaskSchema = new Schema({
name : String,
lastPerformed : Date,
folder : String,
user : {type: Schema.ObjectId, ref: 'User'} // assuming you name your model User
});
With this, your query for all users, including arrays of their tasks might be:
User.find({}).populate('tasks').run(function(err, users) {
// do something
});
Of course, this means maintaining the ids in both places. If that bothers you, it may be best to stick to the first method and just get used to writing more complex (but still simple enough) queries.
As of version 4.2.0, mongoose supports single subdocuments.
From the docs:
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
});
What about this simple solution?
var TaskSchema = new Schema({
name : String,
lastPerformed : Date,
folder : String,
user : {
name : String,
app_key : String,
app_secret : String
}
})