Sequelize – query include with belongsToMany or hasMany - postgresql

I have User and Deal models. They are associated with belongsToMany relationship through UserDeals.
Let's say I have a deal like this:
const deal = {
id: 'my-id',
users: [{ id: 'user-a' }, { id: 'user-b' }]
};
How can I find it without having its id but having both users' ids?

Related

Prisma Many-to-Many Relation

model Students {
studentId Int #id #default(autoincrement())
user User? #relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
userId Int? #unique
classCode String?
courses Course[]
}
model Course {
courseId Int #id #default(autoincrement())
courseName String
courseDescription String?
courseInstructor String
classes Classes[]
students Students[]
}
I have data in both tables seperately. I do not want to insert data in one and connect it to other using implicit relation. I just want to connect them in their implicit model by inserting both foreign key in that table by create or insert query using prisma.
for now their implicit created model is empty.
I want to know the query for that in prisma client.
I have made the relations between two models of many to many. I have data in both tables separately and their implicit model in empty. I just want to add the foreign in that table by myself to connect them. I want to know the query for that in prisma client.
What you need here is connect and create. based on prisma documantation you can create query like this.
To connect a student and many course :
await prisma.student.create({
data: {
userId: 1,
classCode: "2"
Course: { connect: [{ courseName: 'programming', courseDescription: "desc", courseInstructor: "Mr/Mrs" }, { courseName: 'development', courseDescription: "desc", courseInstructor: "Mr/Mrs" }] },
},
})
To create a student and many course :
await prisma.student.create({
data: {
userId: 1,
classCode: "2"
Course: { connect: [{ courseName: 'programming', courseDescription: "desc", courseInstructor: "Mr/Mrs" }, { courseName: 'development', courseDescription: "desc", courseInstructor: "Mr/Mrs" }] },
},
})
or you can use connectOrCreate
Reference : https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/working-with-many-to-many-relations

Updating a many-to-many relationship in Prisma

I'm trying to figure out the right way to implement an upsert/update of the following schema:
model Post {
author String #Id
lastUpdated DateTime #default(now())
categories Category[]
}
model Category {
id Int #id
posts Post[]
}
Here is what I'd like to do. Get a post with category ids attached to it and insert it into the schema above.
The following command appears to insert the post
const post = await prisma.post.upsert({
where:{
author: 'TK'
},
update:{
lastUpdated: new Date()
},
create: {
author: 'TK'
}
})
My challenge is how do I also upsert the Category. I'll be getting a list of Catogories in the like 1,2,3 and if they do not exist I need to insert it into the category table and add the post to it. If the category does exist, I need to update the record with the post I inserted above preserving all attached posts.
Would appreciate it if I could be pointed in the right direction.
For the model, it can be simplified as follows as Prisma supports #updatedAt which will automatically update the column:
model Post {
author String #id
lastUpdated DateTime #updatedAt
categories Category[]
}
model Category {
id Int #id
posts Post[]
}
As for the query, it would look like this:
const categories = [
{ create: { id: 1 }, where: { id: 1 } },
{ create: { id: 2 }, where: { id: 2 } },
]
await db.post.upsert({
where: { author: 'author' },
create: {
author: 'author',
categories: {
connectOrCreate: categories,
},
},
update: {
categories: { connectOrCreate: categories },
},
})
connectOrCreate will create if not present and add the categories to the posts as well.

Multiple subdocuments in Mongoose

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.

Populating Mongoose virtual that aggregates two different collections

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];
}
);
}

Sails.js Waterline: User, Role, Task models, Please give me some suggestions on my final model design

This question might be related to database schema design in sails.js waterline.
One task need to record different roles of users. For example, a Task need to know who are fooUsers and barUsers in this task. What is the best way to handle this? (foo and bar are different roles for a user)
More Context
A User will have different Roles.
The roles of the user might change in the future.
A User will only be one Role in one Task.
The Role of a User in a specific Task will never change.
A Task will have many users involved.
All tasks are the SAME type.
The total number of Roles will NOT change.
For example:
One task has four users: UserA with Role1, UserB with Role2, UserC with Role3, UserD with Role1.
In the future, UserA might have Role2 and Role3. But that doesn't affect what role of the UserA is in the above task.
My Final Design
// User.js
attributes: {
tasks: {
collection: 'task',
via: 'users'
},
roles: {
collection: 'role',
via: 'users'
},
userTaskRoles: {
collection: 'userTaskRole',
via: 'user'
}
}
// Task.js
attributes: {
users: {
collection: "user",
via: "tasks"
},
userTaskRoles: {
collection: 'userTaskRole',
via: 'task'
}
}
// Role.js
attributes: {
users: {
collection: 'user',
via: 'roles'
},
userTaskRoles: {
collection: 'userTaskRole',
via: 'role'
}
}
// UserTaskRole.js
attributes: {
task: {
model: 'task'
},
user: {
model: 'user'
},
role: {
model: 'role'
}
}
I updated this question a lot. The above is my final design. Please give me some feedback whether or not this is good or bad and are there any better design patterns?
Thanks.
You need to provide more context for your use case. Without knowing why you want to do what you want to do, I can provide this answer.
Differentiate foo / bar with a column in your tasks and reference users in a single column like so:
// User.js
attributes:{
tasks: {
collection: "task",
via: 'users'
}
}
// Task.js
attributes: {
Users: {
collection: "user",
via: "tasks"
},
type: {
type: 'string',
enum: ['foo','bar']
}
}