I have Online Shop, where exists products and category. The product has in model field
category: {
type: Types.ObjectId,
ref: "Category",
required: [true, "Product category is required"]
},
and when i add new product select category ( in list exists ) and add id.
But if remove category, the products have categoryId which does not exists. Is it possible to implement the functionality that used in wordpress. For example if in wordpress i remove category, all posts who had this category, transfer in category uncategorized (This category create automatically and cannot be delete)
If I understood you correctly, you can use mongoose middlewares to "cascade deleting". So when a category is removed, you can to code that every product has now "Uncategorized"
You can use a pre hook when you call delete function and do something like this (not tested with your schema):
category.pre(/(?:delete|remove)/, function(next) {
var id = this.getQuery()._id; //get category _id
product.updateMany({
category: id
},{
$set:{
category:yourUncategorizedId
}
}).then(next()).catch(e => next(e))
})
So, in this case, a regex match is used to go into the hook.
This hook is called when a remove or delete function is called from category model. So, when a category is deleted, this hook will update all references by your uncategorizedId to ensure any product has a non-existing category reference.
Related
I am using Hasura with my Flutter Application..
I have 2 tables: tasks and categories
tasks comprises of id, task_name, category_id, status.
category comprises of id, category_name, user_id, color.
What I want to do is get the name of the category that the task belongs to using the category_id
What I thought of is:
query getTasks($user_id: String!) {
tasks(where: user_id: {_eq: $user_id}}, order_by: {created_at: desc}) {
category_id
name
}
category_by_pk(id: tasks['category_id']){
name
}
}
The part that is tasks['category_id'] being passed as a query variablele is giving an error
Any idea how can I do this?
Thanks in advance
Have you tracked a relationship in Hasura between tasks and categories? Normally with GraphQL you would just traverse the relationship to get information about the related entity:
query getTasks($user_id: String!) {
tasks(where: user_id: {_eq: $user_id}}, order_by: {created_at: desc}) {
name
category { // Just follow the relationship
id
name
}
}
}
I use Hasura and I have a social-network like situation.
In which I have a "User" object and a "Feed" object.
Every user has a feed.
I have a relationship from user.id to feed.id.
The relevant mutation is UpsertUserDetails as follows:
mutation UserDetailsUpsert(
$email: String!
$picture: String
) {
insert_users_one(
object: {
email: $email
feed: { data: {} }
picture: $picture
}
on_conflict: { constraint: users_tid_email_key, update_columns: [picture] }
) {
id
}
}
So when I create a new user it also creates a feed for it.
But when I only update user details I don't want it to create a new feed.
I would like to stop the upsert from going through to relationships instead of the above default behavior.
and according to this manual I don't see if its even possible: https://hasura.io/docs/latest/graphql/core/databases/postgres/mutations/upsert.html#upsert-in-nested-mutations
To allow upserting in nested cases, set update_columns: []. By doing this, in case of a conflict, the conflicted column/s will be updated with the new value (which is the same values as they had before and hence will effectively leave them unchanged) and will allow the upsert to go through.
Thanks!
I'd recommend that you design your schema such that bad data cannot be entered in the first place. You can put partial unique indices on the feed table in order to prevent duplicate feeds from ever being created. Since you have both users and groups you can implement it with 2 partial indices.
CREATE UNIQUE INDEX unique_feed_per_user ON feed (user_id)
WHERE user_id IS NOT NULL;
CREATE UNIQUE INDEX unique_feed_per_group ON feed (group_id)
WHERE group_id IS NOT NULL;
I have a group of checkboxes for skin concerns. Users can check/uncheck them before submitting, which means the set of skin concerns submitted can be different every time.
I modeled it in Prisma schema as an 'explicit' many-to-many relation.
model User {
id String #id #default(cuid())
name String?
nickname String? #unique
...
skinConcerns SkinConcernsForUsers[]
...
}
model SkinConcern {
id Int #id #default(autoincrement())
name String #unique
user SkinConcernsForUsers[]
}
model SkinConcernsForUsers {
user User #relation(fields: [userId], references: [id])
userId String
skinConcern SkinConcern #relation(fields: [skinConcernId], references: [id])
skinConcernId Int
##id([userId, skinConcernId])
}
Then, SkinConcerns table is seeded with the following values, using prisma.skinConcern.createMany:
"ACNE",
"DRYNESS",
"OILY_SKIN",
"PIGMENTATION",
"REDNESS",
"WRINKLES",
SkinConcerns in Update mutation input comes in the form of array of strings, e.g. ["PIGMENTATION", "REDNESS"].
I want to update the skin concerns for users (SkinConcernsForUsers) from the prisma.user.update query, but it's tricky, since I'm not merely creating SkinConcerns, but have to connect to existing set of skin concerns.
I've tried directly setting skinConcerns in user, like
await prisma.user.update({
where: { nickname },
data: {
// ... other user data
skinConcerns: {
set: [
{
skinConcern: {
connect: { name: "PIGMENTATION" },
},
},
{
skinConcern: {
connect: { name: "REDNESS" },
},
},
],
},
// ... other user data
}
});
among many other things, but of course this is not a correct argument and fails with error
Unknown arg `connect` in data.skinConcerns.update.0.where.connect for type SkinConcernsForUsersWhereUniqueInput. Did you mean `select`?
Argument data for data.skinConcerns.update.0.data is missing.
Unknown arg `connect` in data.skinConcerns.update.1.where.connect for type SkinConcernsForUsersWhereUniqueInput. Did you mean `select`?
Argument data for data.skinConcerns.update.1.data is missing.
Is there a way to do this? Is it even possible to update this in prisma.user.update?
I guess I could directly update SkinConcernsForUsers. In that case, should I just delete all rows associated to the user that are not in the user input ["PIGMENTATION", "REDNESS"], then create rows that don't already exist? What will it look like in prisma code?
First I would change your schema for SkinConcern. The id field is not necessary and will create complications in queries (you would needlessly need to map each name to id when trying to connect/disconnect records.
The name field is sufficient as the primary key, as it is always unique for a certain record.
The changed schema looks like this
model SkinConcern {
name String #id // name is the new #id.
user SkinConcernsForUsers[]
}
model SkinConcernsForUsers {
user User #relation(fields: [userId], references: [id])
userId String
skinConcern SkinConcern #relation(fields: [skinConcernName], references: [name])
skinConcernName String
##id([userId, skinConcernName])
}
The query you want to do can be executed in two steps with the SkinConcernsForUsers model.
Step 1: Remove existing SkinConcernsForUsers records a user is connected to. These are no longer relevant, as you want to overwrite the previous selection.
Step 2: Create new SkinConcernsForUsers records with the new choices.
Here is what the code looks like
// step 1
await prisma.skinConcernsForUsers.deleteMany({
where: {
userId: "1",
},
});
// step 2
await prisma.skinConcernsForUsers.createMany({
data: [
{
userId: "1",
skinConcernName: "REDNESS",
},
{
userId: "1",
skinConcernName: "PIGMENTATION",
},
],
});
I have 2 models: Supplier and Supplier type. The client should be able to retrieve all Suppliers belonging to a particular Supplier Type:
Supplier:
name: {
type: String,
required: true,
minlength: 2,
maxlength: 255,
},
...
supplier_type: { type: ObjectId, ref: 'SupplierType' },
}
SupplierType:
name: {
type: String,
required: true,
minlength: 2,
maxlength: 50,
},
suppliers: [{ type: ObjectId, ref: 'Supplier' }],
};
Design 1:
A field on Supplier contains a reference to the assigned Supplier Type object.
An array on the Supplier Type object contains object references to all the Suppliers that have the Supplier Type.
The client queries the relevant Supplier Type document, let's say Vegetables and contained in the response, among other fields, will be a list of Suppliers.
With this approach, each time a new Supplier is saved, at least one other DB operation would be needed to update the Suppliers array on the Supplier Type object.
Design 2:
Remove the Suppliers reference array from Supplier Type object
A field on Supplier contains a reference to the assigned Supplier Type object, as in design 1.
The Client, this time, queries the Supplier document with a parameter specifying the Supplier Type i.e. GET /suppliers?supplier-type=Vegetables
Which design makes the most sense/would be the recommended approach in MongoDB?
I see no reason to split these two objects into separate collections. Embed the SupplierType inside the Supplier object. If a Supplier can be of more than one SupplyType you can extend that object into an array.
Now when you get any Supplier you automatically get their SupplierType without the need for a join and/or a second query.
Want to find all the Supplier docs by SupplyType? Query on the SupplyType field and a single cursor will return all the relevant suppliers.
Apply indexes to either field if the number of items is large to improve performance.
As shown in that stackoverflow answer, having no support for cascading (cascading deletes in particular) in Waterline there is a workaround for one-to-many associations by using the afterDestroy (or afterUpdate for soft-delete) lifecycle callback and deleting the associated records with a second query. This is possible via ManyModel.destroy({ oneModel: _.pluck(destroyedOneModels, "id") }) from within afterDestroy.
How do we do that for a many-to-many relationship (having in mind that a junction table is used internally and we have to delete records from it)?
I did some tests using the Pet / User example from the documentation with sails 0.11.
Writting this lifecycle callback in the Pet model deletes all the users associated to a pet before deleting it.
// models/Pet.js
module.exports = {
attributes: {
name:'string',
color:'string',
owners: {
collection: 'user',
via: 'pets'
}
},
beforeDestroy: function(criteria, cb) {
// Destroy any user associated to a deleted pet
Pet.find(criteria).populate('owners').exec(function (err, pets){
if (err) return cb(err);
pets.forEach(function(recordToDestroy) {
User.destroy({id: _.pluck(recordToDestroy.owners, 'id')}).exec(function(err) {
console.log('The users associated to the pet ' + recordToDestroy.name + ' have been deleted');
});
});
cb();
})
}
};
I couldn't do it in the afterDestroy lifecycle callback because the many-to-many properties of the deleted records are missing there.
Waterline is deleting the records of the junction table automatically.
The problem with this feature is that it probably would delete too much things if some pets share some owners. Following the example of the documentation, if you delete the pet Rainbow Dash, you will delete the users Mike, Cody and Gabe, and the pets Pinkie Pie and Applejack would be orphans.
If you define a many-to-many relation like this one but you know that the pets cannot have any owner in common, then it works fine. Otherwise, you should add a test to check that you will not make another pet an orphan.