How to include a flexible object inside a mongoose schema? - node.js

I have a mongoose schema similar to this:
var Schema = new mongoose.Schema({
flexible_object: // some object
// a bunch of other not flexible stuff
})
For a set of complicated reasons, I do not know the structure of the flexible object.
How could I store an object in mongoose like this?
Also, I can't use JSON.stringify() to store it as a string because I want to be able to query it.

As mentioned in the comment you need a mixed schema type.
You can simply do it by defining an empty object:
var Schema = new mongoose.Schema({
flexible_object: {},
// a bunch of other not flexible stuff
})

Related

Dynamic Mongoose Field Type

I am developing an app where a user could store his model on a database using mongoDB and mongoose. Taken from mongoose tutorial the type of the field has to be defined. For example here we have to define that the name is a string.
const personSchema = new mongoose.Schema({
name: String
});
const Person = mongoose.model('Person', personSchema);
Is there any way to make it dynamic to user's input. I want to create a form where a user will enter a field name and select one of the field types that Mongoose offers [String,Number,Date etc], but I cannot figure any way to implement it. To be honest I don't know even if this is a good approach. An alternative would be to pass everything as a String and serialise the input in order to store it. I want to achieve something like that:
const {fieldName,fieldType} = userInput;
const customSchema = new mongoose.Schema({
fieldName: fieldType
});
const CustomModel = mongoose.model('CustomSchema', customSchema);
Is this possible or should I implement another approach? An alternative would be to pass everything as a String and serialise the input in order to store it.
Thank you in advance!
If I understand you correctly it should work like that:
User defines the model to store
Schema is created using the data provided by the user
User can pass the data to store using the previously created model which will validate the user's input later
In fact, I'm working on a project that has the same functionality. Here is how we did it.
A user sends the model and we store it as a string since we need to have the ability to create the model once again.
When the user passes new data to store using the created model we get the string from mongo and parse it to create the schema. This operation is relatively easy (but depends on what you want to achieve as it can get tricky if you want to have some advanced validation) as you have to just create an object with correct values from mongoose. Something like this for every field that the user has defined.
export const fieldConverter = ({name, type}) => {
switch (type) {
case 'String':
return { [name]: String };
case 'Number':
return { [name]: Number };
...
}
When you have your object ready then you can create a model out of it.
The line with accessing your model from mongoose.models is important as the mongoose will cache the model and throw an error if you try to create it once again.
const DatasetModel =
mongoose.models["your-model-name"] ??
mongoose.model("your-model-name", new mongoose.Schema(schema));
Now when you have the model the rest is just like with the normally created one.
This approach worked for us so I'm adding this as inspiration maybe it will help you. If you have any specific questions about the implementation feel free to ask I will be happy to help.
There is also a Mixed type in mongoose if you don't need the validation later. You can check it here: https://mongoosejs.com/docs/schematypes.html#mixed
You can use Schema.Types.Mixed, An "anything goes" SchemaType. Mongoose will not do any casting on mixed paths.
let customSchema = new Schema({custom: Schema.Types.Mixed})
Read more about it here
After some research I figure at that mongoose type can also be strings. For example
const personSchema = new mongoose.Schema({
name: "String"
});
const Person = mongoose.model('Person', personSchema);
Mongoose will handle it

Using Joi schema to get a json object with fields specified in the schema

I am not sure if this type of question has been asked before, but I was not able to find anything related to this. In my current project we use Joi schemas to perform the validations. I like the ability to define custom schemas and run validations on the incoming objects using that schema. I have a task where I need to filter out object properties. Something similar to _.pick but the properties are complex and deal with nested objets and arrays. We already have a joi schemas that we have designed to perform validations but I am thinking of using the same to get the specific properties of the object, like filtering object data using that schema. Something like this:
const Joi = require('joi');
const val = {
a: 'test-val1',
b: 'test-val2'
}
const schema = Joi.object({
a: Joi.string()
});
// now the below result have the object with both `a` and `b`
// properties but I want joi to strip the `b` property from the object
const result = schema.validate(value, { allowUnknown: true });
Joi's documentation doesn't mention anything like this. I have come across this(ajv) library which does do what I want but I wanted to know for sure if this can not be achieved using Joi. Thanks in advance.
Joi offers stripUnkown property that can be used to get only the fields defined in the schema.

Mongoose - Defining a model for a preexisting collection

I have imported some CSV data to my database through mongoimport, which created my collection during the import.
When defining my model in Node, what do I pass for the schema parameter? Viewing my db in compass shows a schema already created based off the imported data.
I'm currently passing an empty schema which seems totally wrong.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Units = new Schema({
});
module.exports = mongoose.model('Units', Units, 'units');
The schema should contain something like this that defines the kind of data you're working with.
var Units = new Schema({
f_name: String,
l_name: String,
manager: Boolean
});
See 'Defining your schema'.
Also, I don't believe mongoose.model takes a third parameter.
module.exports = mongoose.model('Units',Units);
Edit: yes it does.

Creating Mongoose Schemas with or without 'new' keyword?

Most of the examples I have seen online do something like...
var UserSchema = new mongoose.Schema({
name: String,
age: String
});
However, recently I found a book do the above... but without the new keyword.
var UserSchema = mongoose.Schema({
name: String,
age: String
});
I am now confused. Do we use the new keyword for creating the schema or not.. and what happens in both cases?
Both are valid and returns a new instance of the Mongoose.Schema class. What this means is that both does exactly the same. This line checks whether you already have an instance of the Schema class, if not, it returns one for you.
To summarize, if you call
var schema = new mongoose.Schema({})
you initialize an instance yourself, while if you call
var schema = mongoose.Schema({})
mongoose initializes one for you, with this:
function Schema(obj, options) {
if (!(this instanceof Schema)) {
return new Schema(obj, options);
}
...
Yes, I agree with the above answer. But here I would like to add some more important points to it. Looking at the Mongoose documentation, I feel like it should always be called with the new keyword since Schema is a constructor.
Both will work and are valid, but my suggestion is to only use the new mongoose.Schema() since it's the correct way. Following standard conventions will make your code easier to read; not only for others but for you as well when you go back to it in 6 months.

Unable to get the value of particular key in mongoose if that key is not present in Schema

UserEventsInfo = new mongoose.Schema({
name: String,
username: String,
event_movie:[String],
event_tour:[String],
event_restaurant:[String],
event_lifetimeevents:[String]
},{strict : false});
I am able to insert new key-value pair other than defined in the schema
but when I try to read the value of that key. I can't. I am using the following code.
UserEventsDetails.find({username:username},function(err,docs){
if(!docs.length)
{
res.send('datanotavailable');
}
else{
res.send(docs[0][eventname]);
}
});
Here eventname is a variable.
When I add that key in the schema it returns the value i.e. work's fine.
Otherwise it is not returning any value.
Looks like there was an issue submitted like this to mongoose. Here is there response:
The benefit we see in a schemaless database is the ability for our data model to evolve as fast as our features require it, without a linear impact on performance and slower deployment cycles with needless migrations.
If you don't want your data to be normalized and validated prior to saving, then you don't need a tool like Mongoose, you can use the driver directly.
After a little digging there is a way to do this, but you will need to have a field with type Schema.Types.Mixed. So it would look like this:
var schema = new Schema({
mixed: Schema.Types.Mixed,
});
var Thing = mongoose.model('Thing', schema);
var m = new Thing;
m.mixed = { any: { thing: 'i want' } };
m.save(callback);
To do a find on a mixed this SO question answers that.
****EDIT
forgot to link the documentation of mixed types

Resources