Loopback include relations and where - node.js

I have two loopback models: user and backendUser. One (user) is stored with loopback and the other one (backendUser) is stored on a distant mysql database. They have a relation togerther based on a field user has (backendUserId) corresponding to the ID field of backendUser
There is a case where I get every user with its corresponding backendUser. But I would like to get the users depending on some backendUser values. Example: I need every user where the backendUser.role is 4.
I use this filter to have the expected result:
{ include: { relation: "backendUser", scope: { where: { "role" : 4 } } } }
My issue is that I get an array of every user but I only get related backendUser when they have role to 4. The where is only applying on backendUser not on the whole entity.
I don't know if it's clear, but I really need this.

Use the following:
{
where: {
role: 4,
include: {
relation: "backendUser",
scope: {
where: { "role": 4 }
}
}
}
}

{
where: {
// here specific object of query user
},
include: [
{
relation: 'backendUser',
scope: {
where: {
role: 4,
},
},
},
],
}

Related

How do you exclude and include fields in Sequelize by scope?

const getMe = await UserModel.scope("test").findOne({
where: {
uid: uid,
},
include: [
{
model: GroupModel,
as: "groups",
include: ["product"],
},
],
});
I am trying to manage excluding fields and allowing fields based on scope.
defaultScope: {
attributes: {
exclude: ["id"],
},
},
scopes: {
test: {
atrributes: {
exclude: ["email"],
},
},
},
associations
UserModel.hasMany(GroupModel, { as: "groups" });
Groupmodel.belongsTo(UserModel, {
foreignKey: "userId",
as: "user",
});
GroupModel.belongsTo(ProductModel, {
foreignKey: "productId",
as: "product",
});
As a test I am by default excluding "id", and with the test scope I am excluding "email". I have tried everything from exclude include setting attributes directly in the findOne call. Nothing works.
What is the proper way to exclude certain fields say for "Public" returns, and include all fields for an "Admin scope" of some sort?
If you have defaultScope like this.
defaultScope: {
attributes: {
exclude: ['email']
}
}
When you do find query, it excludes "email" by default and use unscoped to disable the defaultScope.
// This should not return email
UserModel.findOne()
// Admin case: unscoped to disable defaultScope. This should return email.
UserModel.unscoped().findOne()
Alternatively, if you would like to be more explicit, you can have scope named "admin".
{
defaultScope: {
attributes: {
exclude: ['email']
}
},
scopes: {
admin: {} // No special options for admin scope. No exclusion.
}
}
This way when you do find query, it is excluding "email" by default. Then, if you use "admin" scope, it won't exclude anything.
// This should not return email
UserModel.findOne()
// Admin case: This should overwrite the defaultScope.
UserModel.scope('admin').findOne()
.scope(str) function overwrites the defaultScope, so any options in defaultScope is ignored when you use .scope(str).

How do I use "AND" operator with multiple query parameters in nested relation when querying in Prisma?

Its my first time trying prisma and am stuck. So I have "products" and "filters" model.
I want the following query to work. The idea is, I want to fetch the products with dynamic matching query params (name and value). The product query parameters come dynamically from the frontend.
const products = await prisma.product.findMany({
where: {
categoryName,
subCategoryName,
filters: {
some: {
AND: [
{
name: "RAM",
value: "32GB",
},
{
name: "Storage",
value: "1TB",
},
],
},
},
},
include: {
images: true,
},
});
If there's only one parameter, like
{
name:"RAM",
value:"32GB"
}
the query returns appropriate products, but if there are more that one query params (like in the original code above), it returns empty array.
my product schema looks like this, simplified,
name String
filters Filter[]
my filter schema looks like this, simplified
name String
value String?
product Product? #relation(fields: [productId], references:[id])
productId Int?
Thank you very much
I've found the solution here
https://github.com/prisma/prisma/discussions/8216#discussioncomment-992302
It should be like this instead apparently.
await prisma.product.findMany({
where: {
AND: [
{ price: 21.99 },
{ filters: { some: { name: 'ram', value: '8GB' } } },
{ filters: { some: { name: 'storage', value: '256GB' } } },
],
},
})

Sequelize How to delete an occurence in an association table

I'm having two tables user and group, a manytomany relation between them, thereofre a third table group_has_user
given a user I'm trying to remove some groups,
I tried :
Model.User.findOne({
where: {
"id": POST.id
},
include: [Model.Project]
}).then(function (user) {
user.Project.destroy({
where:{
"id": ids
}
})
})
where id is the user id and ids, is a list of groups that I wan to
remove
but this code doesn't works, project is undefined, and also I don't think that what I want could be done this way, can anyone help ?
You can get the Projects you want to remove and then call the generated removeProject method on each project you got:
Model.User.findOne({
where: {
"id": POST.id
},
include: [Model.Project]
}).then(function (user) {
Model.Projcet.find({
where: {
id: {
[Op.in]: ids
}
}
}).then(projectsToRemove => {
projectsToRemove.forEach(p => {
await user.removeProject(p);
})
})
})
Source: Official documentation

Conditional update, depending on field matched

Say I have a collection of documents, each one managing a discussion between a teacher and a student:
{
_id,
teacherId,
studentId,
teacherLastMessage,
studentLastMessage
}
I will get queries with 3 parameters: an _id, a userId and a message.
I'm looking for a way to update the teacherLastMessage field or studentLastMessage field depending on which one the user is.
At the moment, I have this:
return Promise.all([
// if user is teacher, set teacherLastMessage
db.collection('discussions').findOneAndUpdate({
teacherId: userId,
_id
}, {
$set: {
teacherLastMessage: message
}
}, {
returnOriginal: false
}),
// if user is student, set studentLastMessage
db.collection('discussions').findOneAndUpdate({
studentId: userId,
_id
}, {
$set: {
studentLastMessage: message
}
}, {
returnOriginal: false
})
]).then((results) => {
results = results.filter((result) => result.value);
if (!results.length) {
throw new Error('No matching document');
}
return results[0].value;
});
Is there a way to tell mongo to make a conditional update, based on the field matched? Something like this:
db.collection('discussions').findOneAndUpdate({
$or: [{
teacherId: userId
}, {
studentId: userId
}],
_id
}, {
$set: {
// if field matched was studentId, set studentLastMessage
// if field matched was teacherId, set teacherLastMessage
}
});
Surely it must be possible with mongo 3.2?
What you want would require referencing other fields inside of $set. This is currently impossible. Refer to this ticket as an example.
First of all, your current approach with two update queries looks just fine to me. You can continue using that, just make sure that you have the right indexes in place. Namely, to get the best performance for these updates, you should have two compound indexes:
{ _id: 1, teacherId: 1 }
{ _id: 1, studentId: 1 }.
To look at this from another perspective, you should probably restructure your data. For example:
{
_id: '...',
users: [
{
userId: '...',
userType: 'student',
lastMessage: 'lorem ipsum'
},
{
userId: '...',
userType: 'teacher',
lastMessage: 'dolor sit amet'
}
]
}
This would allow you to perform your update with a single query.
Your data structure is a bit weird, unless you have a specific business case which requires the data the be molded that way i would suggest creating a usertype unless a user can both be a teacher and a student then keep your structure.
The $set{} param can take a object, my suggestion is to do your business logic prior. You should already know prior to your update if the update is going to be for a teacher or student - some sort of variable should be set / authentication level to distinguish teachers from students. Perhaps on a successful login in the callback you could set a cookie/local storage. Regardless - if you have the current type of user, then you could build your object earlier, so make an object literal with the properties you need based on the user type.
So
if(student)
{
var updateObj = { studentLastMsg: msg }
}
else
{
var updateObj = { teacherLastMsg: msg }
}
Then pass in your update for the $set{updateObj} I'll make this a snippet - on mobile

Sequelize and querying on complex relations

I'm trying to understand how best to perform a query over multiple entities using Sequelize and Node.js.
I have defined a model "User" which has a belongsToMany relation with a model "Location". I then have a model "Asset" which also has a belongsToMany relation with "Location". When I have an instance of a User I would like to fetch all Assets that are associated with Locations that the User is associated with.
I tried the following which doesn't seem to work...
user.getLocations().then(function(userLocations) { return Asset.findAll({ where: { "Locations" : { $any : userLocations } }) })
Could anyone offer any suggestions?
Try this query:
User.findById(user_id, {
include: [{
model: Location,
required: true
}]
}).then(user => Asset.findAll({
where: {
user_id: user.id,
location_id: {
$in: user.locations.map(location => location.id)
}
}
})).then(assets => {
// The rest of your logic here...
});
This was the final result...
User.findById(user_id, {
include: [{
model: Location,
as: 'Locations', // Was needed since the original relation was defined with 'as'
required: true
}]
}).then(user => Asset.findAll({
include: [{
model: Location,
as: 'Locations',
where: {
id: {
$in: user.Locations.map(location => location.id)
}
}
}]
})).then(assets => {
// The rest of your logic here...
});

Resources