directus / directus

The Modern Data Stack 🐰 — Directus is an instant REST+GraphQL API and intuitive no-code data collaboration app for any SQL database.
https://directus.io
Other
26.95k stars 3.77k forks source link

Limit Handling Issue in Relation Update When Deleting Items #22171

Open mercs600 opened 4 months ago

mercs600 commented 4 months ago

Describe the Bug

I've encountered an issue with Directus service layer when attempting to update relations by deleting items. Specifically, while updating relations with a payload that includes item deletion, only the first 100 related items are being deleted due to default limit constraints.

The behavior is observed in Directus version 10.10.3. The issue seems to be with how limits are handled or not handled when updating relations, specifically for deletion operations. For creation operations, similar logic using a deep update with a _limit: -1 works as expected and allows creating more than 100 relation items.

It would be beneficial to have consistent handling of limits for both creation and deletion of relation items to ensure full flexibility in managing relations through the Directus services layer.

To Reproduce

  1. Create a relation between two collections that can have more than 100 related items.
  2. Try to update a relation by deleting more than 100 items at once using the updateOne method in the ItemsService.

Expected Behavior All specified related items should be deleted from the relation, regardless of the number exceeding the default limit.

Actual Behavior Only the first 100 related items are being deleted when the relation is updated. This seems to be a limitation not present during the creation of relations.

Example code

// Assuming promotionService is an instance of ItemsService configured for a 'promotions' collection
await promotionService.updateOne(promotionId, {
    related_items: { // Assuming 'related_items' is the field representing the relation
        delete: arrayOfItemIdsExceeding100 // An array of more than 100 item IDs to be deleted from the relation
    }
}, {
    deep: {
        related_items: {
            _limit: -1 // Attempting to bypass the default limit (does not work as expected)
        }
    }
});

Directus Version

v10.10.3

Hosting Strategy

Self-Hosted (Custom)

hanneskuettner commented 4 months ago

Looking at the API of the ItemService neither the updateMany/updateOne nor the createMany/createOne offer the query parameter with the deep property you are trying to use. So the additional parameters you're passing are getting ignored.

The reason the create/update operation is working is that it is sequentially processing each related item one at a time, setting the foreign key to the parent item. Since this is a sequential operations we don't run into any query limit as at most one item is processed at the time.

The reason the delete isn't working is that instead of sequentially updating each item and setting its foreign key to null we do a bulk update/delete for all objects matching the keys you are trying to delete, which gets limited by the default query limit. As a workaround, until we decide if we want / can support overriding the limit in such cases, you can manually run a nullify operation as:

const query = {
    filter: {
        _and: [
            {
                [FOREIGN_KEY_FIELD_OF_YOUR_RELATED_ITEMS]: {
                    _eq: parent,
                },
            },
            {
                [ID_FIELD_OF_YOUR_RELATED_ITEMS]: {
                    _nin: savedPrimaryKeys,
                },
            },
        ],
    },
};

const itemsService = new ItemsService('your_related_collection', ...);
itemService.updateByQuery(query, { [FOREIGN_KEY_FIELD_OF_YOUR_RELATED_ITEMS]: null });

(Mind you this is untested code, following the lines of https://github.com/directus/directus/blob/f1ad752826acf85914c26e340f5316a52e1105e3/api/src/services/payload.ts#L761)