Is it possible to have multiple different subdocuments is Mongoose ? I'm creating app for online tests , and each test will have questions array containing different question types , for example true/false , multiple choice, matching and etc ... I want to create different questions schemas and questions array to contain them all. For example questions: [QuestionSchema1, QuestionSchema2]. Is it possible to do so ?
Schema example with basic question type down below. And what if i want do add different type of question for this test ?
const testSchema = new mongoose.Schema({
name: {
type: String
},
level: {
type: String
},
questions: [
{
id: {
type: String
},
question: {
type: String
},
answers: [
{
id: {
type: String
},
answer: {
type: String
}
}
],
validAnswerId: {
type: String
}
}
]
});
If you really want to do this with subdocuments you can just type the the questions as an object array and put whatever you want inside it:
...
questions: [{
type: Object
}],
...
If you are fine with creating multiple collections you can use mongooses refPath to do this with stricter schemas:
...
questions: [{
question: {
type: ObjectId,
refPath: 'questions.questionType'
},
questionType: {
type: String,
enum: ['MultipleChoice', 'Matching', ...]
},
}]
...
Then you can create all the different schemas you want for your questions and add their models (like 'MultipleChoice' and 'Matching') to the questionType enum. Afterwards when you need to access the questions you just populate them with .populate('questions') on the query object.
Related
I am looking for the best way to model this scenario:
There is a ProductA model. Users can "like" or "dislike" ProductA documents. The documents are then added to an array in the User model called "likes" & "dislikes."
var UserSchema = new mongoose.Schema({
...,
likes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ProductA' }],
dislikes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ProductA' }],
...,
});
Now, I have realized that I want to add in a new Product: "ProductB." How do I restructure this database to keep this scalable and allow me to add new products? I am not sure what the best way to do this would be in MongoDB.
I believe my ideal scenario is the following psuedo-model:
var InteractionSchema= new mongoose.Schema({
product: // anonymous reference to an object
productType: //enum of which product it is
user: // user who made the interaction
interactionType: // like or dislike enum
});
I could not find any reference to how to handle anonymous references in MongoDB however. I would appreciate some advice
If I understand your requirement correctly, you can have three collections at a time:
products (contains all the products)
users (contains user information)
user_product_likes (contains user's like/dislike)
Respective schema can be,
UserInformationSchema :
{
name : {
type: String,
required: false
..
},
..
}
ProductSchema :
{
product_type : {
type: Integer,
},
...
}
InteractionSchema :
{
product_id : {
type: Integer
required: true
},
user_id : {
type: Integer
required: true
},
like : {
type: Boolean
required: false,
default:false
},
dislike : {
type: Booelan,
required: false,
default: false
}
}
My app currently has a bit of a complicated situation that is causing me all sorts of problems. As part of the app we are building recipes, and the recipes are made of ingredients (here simply called "foods" because they can also be used on their own). We have a collection of static recipes and one of dynamic ("user") recipes that are copied from the static version when associated with a user. Technically these recipes all live in one collection but use a discriminator.
However, for foods, we have a database we purchased from a third party, but we also may need to add our own foods. Because updates from the third party database may need to overwrite that database, we need to separate foods from the third party from foods we create. So here we need two entirely separate collections, which we'll call "foodsTP" and "foodsAdmin". However, because these should function exactly the same way from a user's perspective, we don't want the front end to care which collection the foods are coming from.
When getting foods directly, this isn't a problem. We have a virtual getter on the recipe that combines the foods from both collections:
RecipeBaseSchema.virtual('foods').get(function get() {
return this.foodsTP.concat(this.foodsAdmin);
});
However, once the foods have been added to the recipe, API requests to get the recipe are not correctly populating the food information. I have seen this document about Virtual Populate but it doesn't seem to be what I need here. My virtual here isn't just a reference to one other collection, it's actively combining the references to two other collections. When we pull this combined array of foods, we should be able to get all the food info from both collections. However, this is the error I get in the console:
If you are populating a virtual, you must set the localField and
foreignField options
Is there any way I can do this with my combined virtual array?
EDIT: Here's a simplified Recipe Schema.
const RecipeBaseSchema = new Schema({
name: {
type: String,
required: true,
},
foodsTP: [{
quantity: {
type: Number,
default: 1,
},
measureUnit: String,
food: {
type: Schema.Types.ObjectId,
ref: 'Food',
},
}],
foodsAdmin: [{
quantity: {
type: Number,
default: 1,
},
measureUnit: String,
food: {
type: Schema.Types.ObjectId,
ref: 'FoodAdmin',
},
}],
dateAdded: Date,
dateModified: Date,
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
}); // options
const RecipeUserSchema = new Schema({});
const RecipeAdminSchema = new Schema({});
API query:
export function getRecipeUser(params) {
params.populate = [
{
path: 'foods',
populate: { path: 'food' },
},
];
params.query = {
_id: params.recipeId,
};
return apiCall('get', `/recipeuser`, omit(params, ['recipeId'])).then(
recipesUser => {
console.log(recipesUser);
return recipesUser[0];
}
);
}
I created a simple dynamic fields in React-Redux with a plus button to add as many field as I want (hobbies) of an already existing form. I'm using mongodb as a database and so I have this error that tells me that my fields/data don't have iDs.
so how can I generate iDs for my data?
this below is my model with featherJs. as you can see this is how I added my hobbies array in the existing model called myService. I can see that my hobbies are created in mongo (using Robo 3T) which is great but i'm having difficulty reusing them (hobbies) in an other component in Redux. I'm not sure if I should give IDs to this fields or create a new service just for them. I never coded something in backend so I'm confused. what's the rule for this kind of situations.
Any other suggestions would be helpful.
warning in Redux: Each child in a list should have a unique "key" prop.
error in api : Cast to ObjectId failed for value at path "_id" for model "
const { Schema } = mongooseClient;
const myService = new Schema({
type: { type: String, enum: VALID_TYPES, required: true },
user: {
type: mongooseClient.Schema.Types.ObjectId,
ref: 'user',
required: true
},
comment: String,
hobbies: [{
type: mongooseClient.Schema.Types.ObjectId,
ref: 'hobbies',
default: [],
required: false }],
date: {
begin: { type: Date, default: Date.now },
current: { type: Date, default: Date.now },
end: { type: Date, required: true },
},
}, {
timestamps: true
});
return mongooseClient.model('myService', myService);
};
So I have practically worked with relational databases only.
Now I'd like to do my first real project with mongo.
I need a role-based access to my app.
In a relational DB, I'd probably put a column 'role' in my 'user' table, having 'role' being referenced by a table of roles.
Maybe this is not quite right for a NOSQL db?
In other words, is it best practice to store the Role just as a string?
E.g.
var UserSchema = new Schema({
username: { type: String, required: true },
password: { type: String, required: true },
role: { type: String }
});
or would it be something like:
var UserSchema = new Schema({
username: { type: String, required: true },
password: { type: String, required: true },
role: { type: Role }
});
(if that's possible at all)
you could have another collection called authority that holds the different authoritys and their names as a string like :
{
"_id": ObjectId("51644d0884969b4512"),
"authority": "ROLE_USER",
}
and then your user-object has a field with a refference
{
"_id": ObjectId("5123432969d434553"),
"password": "notSave",
"name":"user0",
"role:ObjectId("51644d0884969b4512")
}
and your code would be sth like
user = db.user.find({name:"user0"});
userRole = db.authority.find({"_id":user.id})
but if you wanted to get all user with role admin for example, then you need to write iterative code
i made the experience that this can get very messy, especially while iterating over more collections with abstract querys, even with ensured indexes the runtime got longer and longer because sth like table-joins in a single query seems to be not possible, or i did not find out
nevertheless i like mongodb, maybe it helps you
nevertheless you can and you better avoid assosiated documents in other collections and store it in the same document
and as a string
var UserSchema = new Schema({
username: { type: String, required: true },
password: { type: String, required: true },
role: { type: String }
});
MongoDB and "joins"
Consider the following Mongoose schema:
new mongoose.Schema({
attributes: [{
key: { type: String, required: true },
references: [{
value: { type: String, required: true },
reference: { type: mongoose.Schema.Types.ObjectId, required: true }
}]
}
});
A document that follows this schema would look like this:
{
attributes: [
{
key: 'age', references: [{ value: '35', reference: 298387adef... }]
},
{
key: 'name', references: [{
value: 'Joe', reference: 13564afde...,
value: 'Joey', reference: 545675cdab...,
}
...
]
}
I'd like to select attributes according to the following conditions:
- the key is name for example
- the attribute with key name has a least one reference with a value Joe.
Ideally, I'd like to AND-chain many of these conditions. For example, {'name': 'Joe'} and {'age': '35'}.
I can't seem to find a way of doing that Mongoose. I've tried the following Mongoose queries without any good results (it gives either false positives or false negatives):
// First query
query.where('attributes.key', attribute.key);
query.where('attributes.references.value', attribute.value);
// Second
query.and([{ 'attributes.key': attribute.key }, { 'attributes.$.references.value': attribute.value }]);
// Third
query.where('attributes', { 'key': attribute.key, 'references.value': { $in: [attribute.value] }});
So how do I do it?
You can use elemMatch to find docs that contain an attributes element that matches multiple terms:
query.elemMatch(attributes, { key: 'name', 'references.value': 'Joe' })
You can't chain multiple elemMatch calls together though, so if you want to AND multiples of these you'd need to explicitly build up a query object using $and and $elemMatch instead of chaining Query method calls.