Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete items by predicate #596

Closed
nechesa opened this issue Jul 25, 2019 · 12 comments
Closed

Delete items by predicate #596

nechesa opened this issue Jul 25, 2019 · 12 comments

Comments

@nechesa
Copy link

nechesa commented Jul 25, 2019

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.

@nechesa nechesa closed this as completed Jul 25, 2019
@nechesa nechesa reopened this Jul 25, 2019
@j82w
Copy link
Contributor

j82w commented Jul 25, 2019

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

@nechesa
Copy link
Author

nechesa commented Jul 25, 2019

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
                       )
                   );
                }
            }

@nechesa
Copy link
Author

nechesa commented Jul 25, 2019

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.

@j82w
Copy link
Contributor

j82w commented Jul 25, 2019

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)));
                }
            }

@nechesa
Copy link
Author

nechesa commented Jul 25, 2019

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

@j82w
Copy link
Contributor

j82w commented Jul 25, 2019

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

@nechesa
Copy link
Author

nechesa commented Jul 26, 2019

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?

@j82w
Copy link
Contributor

j82w commented Jul 26, 2019

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.

@j82w
Copy link
Contributor

j82w commented Jul 26, 2019

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());
        }

@nechesa
Copy link
Author

nechesa commented Jul 29, 2019

Thank you

@nechesa nechesa closed this as completed Jul 29, 2019
@vip32
Copy link

vip32 commented Aug 16, 2019

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

@j82w
Copy link
Contributor

j82w commented Aug 20, 2019

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants