I have these three models:
Task.js
name: String
Question.js
score: Number,
task_id: { type: mongoose.Types.ObjectId, ref: 'Task', alias: 'task' },
problem_id: { type: mongoose.Types.ObjectId, ref: 'Problem', alias: 'problem' },
Problem.js
name: String
How can I count the number of tasks that matches a certain task name and has a question that is connected to a problem that matches a certain problem id?
Related
I'm using Express and Mongoose to do some database actions. When trying to populate a path where the key includes a whitespace, it basically get's ignored.
MongoDB Model:
const OrgCrimeSchema = new Schema({
gracePeriod: { type: Date, default: Date.now() },
Technical: {
description: String,
difficulty: Number,
owner: { type: Schema.Types.ObjectId, ref: 'User' },
},
'Social Engineering': { // This one causes issues
description: String,
difficulty: Number,
owner: { type: Schema.Types.ObjectId, ref: 'User' },
},
});
Find + Populate:
const now = Date.now();
const allOrgCrimes = await OrgCrime.find({ gracePeriod: { $lte: now } })
.populate('Technical.owner', 'name')
.populate('Social Engineering.owner', 'name');
console.log(allOrgCrimes['Social Engineering'][0].owner);
//5fca3b4a86e77b5c8e58b683
console.log(allOrgCrimes['Technical'][0].owner);
// { name: 'npc_alice', _id: 5fae6d7ee60018434108369c }
I assume the path is not being populated because of a white space in the key. I've tried both dot notation and typing {path: 'Social Engineering', select: 'name -id'}, without luck.
Is there any way around this without having to rewrite the schema structure?
In short, there is no way, if we wanted to populate multiple paths at the same time there is this way
for exmaple
Story
.find(...)
.populate('book author') // space delimited path names
.exec()
you can see space delimited path names.
When you pass a key with space,mongoose consider it as populate multiple
As poined out by Mohammad Yaser Ahmadi, there is no solution to this issue today because of populate assumes you're populating multiple instead of trying to read a path name with a space in between. How I 'solved' this was to rewrite the model to this:
const OrgCrimeSchema = new Schema({
gracePeriod: { type: Date, default: Date.now() },
roles: [{
role: String,
description: String,
difficulty: Number,
owner: { type: Schema.Types.ObjectId, ref: 'User' },
}]
})
And then the populate actually get's easier:
.populate('roles.owner', 'name')
After completing a course on MEAN Stack, I'm working on making my own webapp - a recipe page. I've designed my models, but when I try to fill the database with a new Recipe it just won't work. The main issue is the way I want to store ingredients, they'll be stored on one of the tables, then each recipe has its list of ingredients and amounts for each. For example, to make some toast you need, say "50 grs. of Butter and 4 slices of Bread". This is the Schema I'm using:
var RecipeSchema = Schema({
name: String,
desc: String,
author: { type: Schema.ObjectId, ref: 'User' },
category: { type: Schema.ObjectId, ref: 'Category' },
ingredients: [{ amount: String, ingredient: { type: Schema.ObjectId, ref: 'Ingredient' }}],
steps: [String],
image: String,
thumbnail: String,
portions: Number,
difficulty: Number,
cookingTime: Number,
comment: String
});
When I go in Postman and try to fill the "Toast Recipe" entry, I have the following:
name:Toast
desc:Toast is a common breakfast staple.
author:5cad791a7b2e651f7803f5de
category:5cb1ff8f484a172984178a97
ingredients:[{"amount": "4 slices", "ingredient": "5cb1ffdb484a172984178a98"}, {"amount": "35 grs.", "ingredient": "5cb2000d484a172984178a99"}]
steps:['Toast the bread in the oven.','Spread some butter on each toast.']
image:'null'
thumbnail:'null'
portions:1
difficulty:1
cookingTime:15
comment:'null'
But I keep getting a "cast Array" error. What could be the issue? Is it a problem with my model, with Postman, or with the way I'm sending the Array?
EDIT:
Apparently it was a problem whit the way I posted my arrays in Postman. After some more tests I managed to upload a full recipe in JSON format. The structure is correct (but what Juan suggests can be used too, to make the code cleaner).
I'am not completely sure, but when I worked with mongoose, inner objects had a new Schema object inside, in this case it would on ingredients
const IngredientSchema = new Schema({
amount: String,
ingredient: { type: Schema.ObjectId, ref:'Ingredient' }
});
const RecipeSchema = new Schema({
name: String,
desc: String,
author: { type: Schema.ObjectId, ref: 'User' },
category: { type: Schema.ObjectId, ref: 'Category' },
ingredients: [ IngredientSchema ],
steps: [String],
image: String,
thumbnail: String,
portions: Number,
difficulty: Number,
cookingTime: Number,
comment: String
});
this is what I have and it works:
var comboSchema = new Schema({
components: [{
type: Schema.Types.ObjectId,
ref: "Component"
}]
})
This is what I want to achieve:
var comboSchema = new Schema({
components: [{
type: Schema.Types.ObjectId,
ref: "Component",
amount: {type: Integer}
}]
})
Is it possible in MongoDB, if not what is the best workaround?
Thank you :-)
This schema work because of an element or filed name is provided
var comboSchema = new Schema({
components: [{
type: Schema.Types.ObjectId,
ref: "Component"
}]
})
Now you made a single mistak you want to create schema name without name in object with two different filed
Right way to create schema like this is to make other variable inside of array which contain type of filed
var comboSchema = new Schema({
components: [{
id: {
type: Schema.Types.ObjectId,
ref: "Component"
},
amount: { //If you want to make every component amount
type: Number
}
}]
})
Or
var comboSchema = new Schema({
amount: { type: Number },
//If you want to make only single amount on multiple components
components: [{
componentId: {
type: Schema.Types.ObjectId,
ref: "Component"
}
}]
})
But in both case you can't populate directly. You need to use aggregation for that to get data for embedded documents.
I am working on a side project at the moment that will hopefully help understand Mongo. I am coming from a MySQL world so some of the concepts are a bit strange to me at minutes.
My side project is essentially a project organiser, a project can have 3 areas where a a user can upload multiple files/images, project assests, work in progress, and deliverables.
So should I be creating collections for assets, wip and deliverables and then link them to the project? Using some like,
type: Schema.Types.ObjectId,
ref: "projects"
Or should they be part of the projects schema giving each file a type instead making the project schema look something like,
// Create schema
const ProfileSchema = new Schema({
name: {
type: String,
required: true
},
owner: {
type: Schema.Types.ObjectId,
ref: "users"
},
slug: {
type: String,
required: true,
max: 40
},
status: {
type: String,
required: true
},
brief: {
type: String,
default: "No brief given"
},
date_due: {
type: Date
},
created_at: {
type: Date,
default: Date.now
},
files: [
name: {
type: String
},
filepath: {
type: String
},
type: {
type: String
}
]
});
Potentially there will be 1000s of rows in the collection if I were to ever launch the tool
Is there an accepted way of doing what would ordinarily be an 1:n relationship in a relational database?
I am having difficulty coming up with schemas for a school app.
In particular, I am trying to model the relationship between the different kinds of users (e.g. instructors, teaching assistants, and students) with the courses and tutorials that they belong to.
Here are my requirements:
each course will have one to many tutorials;
each course will be taught by one to many instructors;
each course will have one to many students;
each tutorial will have one to many teaching assistants;
each instructor will teach one to many courses;
each teaching assistant may have one to many tutorials in one to many course;
each student will be enrolled in one to many courses;
each student may belong to one tutorial in the course that they are enrolled in;
So far, the following are my schemas for the user, course, and tutorial collections.
var CourseSchema = new mongoose.Schema({
name: { type: String, required: true },
code: { type: String, required: true },
tutorials: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Tutorial' }], // 1
instructors: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }], // 2
students: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }] // 3
});
var TutorialSchema = new mongoose.Schema({
number: { type: String, required: true },
teachingAsst: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }] // 4
});
var UserSchema = new mongoose.Schema({
email: { type: String, lowercase: true },
password: String,
name: {
first: { type: String, lowercase: true },
last: { type: String, lowercase: true }
},
roles: [String] // instrutor, teachingAsst, student
};
The problem lies with my requirements 5 to 8 -- which is more so the relationship from the User to the other models. What could be a good way to model these relationships?
One way, I thought of doing it e.g. req 5 was to add a field to the User schema
instructor: {
courses: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Course' }]
}
But the problem happens when I do e.g. req 6. similarly because it will complicate the queries (e.g. "find all the tutorials in a course that the user is a teaching assistant in").
teachingAsst: {
courses: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Course' }]
tutorials: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Tutorial' }]
}
In your case, Design is many to many relations. So you have two approach for your problem.
Reference Document
Embedded Document
Embedded approach will have duplicate data which is difficult to update and delete where as the read operation will be much efficient due to single query.
In case of the Reference Approach, your data will be demoralized. So, update and delete operation will be easy where as the read operation will have multiple hits on the database.
So, based on the your application requirement you should have to decide the appropriate approach.