Azure-cosmos-dotnet-v3: Delete items by predicate

Created on 25 Jul 2019  路  12Comments  路  Source: Azure/azure-cosmos-dotnet-v3

There is no way to remove items from container by predicate. It issue was resolver by used prev version by with Document class and its properties SelfLink with DeleteDocumentAsync. But is any opportunity to do it in new version. I can't find something similar in newest version.
Thank you for attention.

Most helpful comment

Thank you, I'll check it tomorrow and let you know.

All 12 comments

@nechesa can you provide an example of how it worked in v2?

v2 have an opportunity to remove document by link, link created UriFactory etc, but also FeedResponse can be cast as Document class which have a SelfLink property

            IDocumentQuery<T> query = _client.CreateDocumentQuery<T>(
                    UriFactory.CreateDocumentCollectionUri(_databaseId, collectionId),
                    feedOptions)
                    .Where(predicate)
                    .AsDocumentQuery();

            List<Task> deleteTasks = new List<Task>();
            while (query.HasMoreResults)
            {
                FeedResponse<dynamic> results = await ExecuteWithRetries(() => query.ExecuteNextAsync(), cancellationToken);

                foreach (Document item in results)
                {
                    T model = JsonConvert.DeserializeObject<T>(item.ToString());
                    deleteTasks.Add(
                       ExecuteWithRetries(
                           () =>
                           {
                               var requestOptions = new RequestOptions();

                               // set partition key if exists
                               if (CosmosCollectionsSetting.IsCollectionHasPartitionKey(collectionId))
                               {
                                   requestOptions.PartitionKey = new PartitionKey(item.Id);
                               }


                               return _client.DeleteDocumentAsync(item.SelfLink, requestOptions);
                           },
                           cancellationToken
                       )
                   );
                }
            }

Hello @j82w so, what we can expect with this issues ? Because i guess it's important case, when you need to remove some documents by predicate. Thank you for attention.

Hi @nechesa,

Does this work for your scenario?

 FeedIterator<string> feedIterator = this.Container.GetItemLinqQueryable<T>()
                .Where(predicate)
                .Select(x => x.id).ToFeedIterator<string>();

            while (feedIterator.HasMoreResults)
            {
                FeedResponse<string> results = await feedIterator.ReadNextAsync();
                foreach (var id in results)
                {
                    deleteTasks.Add(
                        this.Container.DeleteItemAsync<T>(id, new PartitionKey(id)));
                }
            }

Thank you, I'll check it tomorrow and let you know.

Depending on your predicate you might also be able to use stored procedures.

Thank you for helping @j82w but code you send is buggy
.Select(x => x.id) we don't know have T.id or not.
Do you have any other option instead procedure?

Every object in Cosmos has an "id" property. It's a required field. I have a few ideas, but I want to validate them first. I'll post an update later today after I get a chance to do some testing.

Here is my current solution. I validated that it works. A solution closer to what you currently have will be possible after PR #604 is merged.

                Expression<Func<Test, bool>> func = (item) => item.id != "test5";
                await DeleteItemsHelper<Test>(container, func);
                Console.WriteLine("delete item helper");
            }
        }

        private class Test
        {
            public string id { get; }
            public int pk { get; }
            public string test { get; }
        }

private interface ItemIdInterface
        {
            string id { get; }
        }

        public async Task DeleteItemsHelper<T>(Container container, Expression<Func<T, bool>> predicate)
        {
            List<Task> deleteTasks = new List<Task>();

// ItemIdInterface does not need to implement type T. It is only use to evaluate the field name "id"
            IQueryable<string> test = container.GetItemLinqQueryable<T>()
                .Where(predicate)
                .Select(item => ((ItemIdInterface)item).id);

            FeedIterator<string> feedIterator = test.ToFeedIterator();
            while (feedIterator.HasMoreResults)
            {
                Microsoft.Azure.Cosmos.FeedResponse<string> results = await feedIterator.ReadNextAsync();
                foreach (var id in results)
                {
                    deleteTasks.Add(container.DeleteItemAsync<dynamic>(id, new Microsoft.Azure.Cosmos.PartitionKey(id)));
                }
            }

            Task.WaitAll(deleteTasks.ToArray());
        }

Thank you

Id as a partitionkey value, really? that's a partition per entity, does not scale

@vip32 there is valid scenarios where users don't need to scale and that the data set will always be small. The example can be modified to return multiple values for both an id and a partition key value.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

darthmolen picture darthmolen  路  4Comments

thomaslevesque picture thomaslevesque  路  7Comments

lbicknese picture lbicknese  路  5Comments

Jubast picture Jubast  路  6Comments

wahyuen picture wahyuen  路  4Comments