Skip to main content

deleteOne

Deletion has the same problems as update โ€” removing a document may require touching many others. This tutorial covers deleting a simple document (a user) and deleting one with dependents (a country), plus the hardCascade option.

Delete a userโ€‹

const deleteUserValidator = () => {
return object({
set: object({
_id: string(),
}),
get: object({
success: optional(enums([0, 1])),
}),
});
};

const deleteUser: ActFn = async (body) => {
const {
set: { _id },
get,
} = body.details;

return await users.deleteOne({
filter: { _id: new ObjectId(_id) },
});
};

coreApp.acts.setAct({
schema: "user",
actName: "deleteUser",
validator: deleteUserValidator(),
fn: deleteUser,
});

The user model's relations (from the previous tutorials):

{
livedCities: {
optional: false,
schemaName: "city",
type: "multiple",
sort: { field: "_id", order: "desc" },
relatedRelations: {
users: {
type: "multiple",
limit: 50,
sort: { field: "_id", order: "desc" },
},
},
},

country: {
optional: false,
schemaName: "country",
type: "single",
relatedRelations: {
users: {
type: "multiple",
limit: 50,
sort: { field: "_id", order: "desc" },
},
},
},
}

There are no relatedRelations pointing at user โ€” nothing embeds a user. That's why deleting a user is simple. But we still have to check its mainRelations: the user is embedded in the users list of the cities it lived in, and in the users list of its country. Lesan removes those embeddings, and if a list needs refilling (it has a limit and a slot opened up), it adds the next qualifying user automatically.

The full example is examples/document/09-1-deleteOne.ts.

The user schema, with all its relations:

User schema relations

Before deleting the user:

User before deleteUser in Compass

While deleting:

User while deleteUser runs

After deleting:

User after deleteUser

Delete a countryโ€‹

Now the hard case. The country model defines no relations of its own โ€” but both city and user create relations with it:

Country schema with cities and users relations

const countryRelations = {};

const cityRelations = {
country: {
optional: false,
schemaName: "country",
type: "single" as RelationDataType,
relatedRelations: {
cities: {
type: "multiple" as RelationDataType,
limit: 50,
sort: { field: "_id", order: "desc" as RelationSortOrderType },
},
citiesByPopulation: {
type: "multiple" as RelationDataType,
limit: 50,
sort: { field: "population", order: "desc" as RelationSortOrderType },
},
capital: { type: "single" as RelationDataType },
},
},
};

const userRelations = {
livedCities: {
optional: false,
schemaName: "city",
type: "multiple",
sort: { field: "_id", order: "desc" },
relatedRelations: {
users: { type: "multiple", limit: 50, sort: { field: "_id", order: "desc" } },
},
},

country: {
optional: false,
schemaName: "country",
type: "single",
relatedRelations: {
users: { type: "multiple", limit: 50, sort: { field: "_id", order: "desc" } },
},
},
};

If we delete a country that still has cities or users referencing it, the data created based on that country becomes meaningless. Lesan refuses โ€” with a relatedRelations error listing every dependent document:

{
"body": {
"message": "please clear below relations status before deletion: [ ... dependent docs ... ]"
},
"success": false
}

We have two ways forward:

  1. Delete all dependent documents one by one.
  2. Set hardCascade: true on deleteOne:
const deleteCountry: ActFn = async (body) => {
const {
set: { _id },
get,
} = body.details;

return await countries.deleteOne({
filter: { _id: new ObjectId(_id) },
hardCascade: true,
});
};

hardCascade: true deletes the country and every dependent document, and the dependents of those dependents, recursively โ€” so cities and users referencing the country are deleted too. The full example is examples/document/09-2-deleteOne.ts.

Before deleting the country:

Country before deleteCountry in Compass

While deleting:

Country while deleteCountry runs

After deleting โ€” the country and its dependent cities and users are gone:

Country after deleteCountry

caution

hardCascade deletes recursively and is irreversible. Use it only when you're sure the dependent documents should be removed with the parent.

Next stepsโ€‹

  • insertMany โ€” inserting many documents while keeping all relations embedded correctly
  • Relations in Depth โ€” limit, sort, excludes, and cascading deletes