2

What I'm trying to do is query by a specific document type

MyType foo = client.CreateDocumentQuery<MyType>(documentCollectionUri)
   .ToList()
   .FirstOrDefault(d => d.SomeProperty == someProperty);

then replace the document with another. It looks like the only methods available are

public Task<ResourceResponse<Document>> ReplaceDocumentAsync(Uri documentUri, object document, RequestOptions options = null);

public Task<ResourceResponse<Document>> ReplaceDocumentAsync(Document document, RequestOptions options = null);

public Task<ResourceResponse<Document>> ReplaceDocumentAsync(string documentLink, object document, RequestOptions options = null);

all of which require the Document or its id.

So, how do I get those values from foo? Not possible?

See Sharp
  • 379
  • 1
  • 4
  • 11

2 Answers2

0

First, you should do your query like this:

List<MyType> foo = client.CreateDocumentQuery<MyType>(documentCollectionUri)
  .Where(d => d.SomeProperty == someProperty);
  .ToList();

Otherwise you will fetch all documents from db before you even filter for your property.

For your actual question, you have two options:

1) Make the id part of your document

CosmosDB will automatically set the Id when saving, and deserialize it to the field when retrieving the document. You can then use the id for the replace operation.

class MyType{

 [Newtonsoft.Json.JsonProperty(PropertyName="id")]      
public string Id {get; set;}
}
// Get documentUri by:
UriFactory.CreateDocumentUri(string databaseId, string collectionId, string documentId)
public Task<ResourceResponse<Document>> ReplaceDocumentAsync(Uri documentUri, object document, RequestOptions options = null);

2) Separate the query definition from the query execution

You can use different types for query definition and response deserialization. So you can query against MyType and deserialize the response as Document.

var query = client.CreateDocumentQuery<MyType>(documentCollectionUri)
  .Where(d => d.SomeProperty == someProperty)
  .AsDocumentQuery();

var response = await query.ExecuteNextAsync<Document>(cancellationToken).ConfigureAwait(false);
var document = response.FirstOrDefault();
Alex AIT
  • 17,361
  • 3
  • 36
  • 73
  • This is a false answer for multiple reasons. First and foremost, You should not use `.ToList()` for `CreateDocumentQuery` but instead use the while has more then `ExecuteNextAsync` method to properly go though Cosmos pagination. Secondly, Just using the `CreateDocumentQuery` method doesn't guarantee that `T` will be of the specific type, but that anything matched in the expression will be mapped to the type. – Nick Chapsas Aug 26 '18 at 17:11
  • Why is ToList false? Maybe it's not as efficient, but if few results are expected there is nothing wrong with it. And of course it does not guarantee that the type matches, I never said it would. – Alex AIT Aug 26 '18 at 22:24
0

First and foremost something is not quite clear by the way you express your question.

MyType foo = client.CreateDocumentQuery<MyType>(documentCollectionUri)
   .ToList()
   .FirstOrDefault(d => d.SomeProperty == someProperty);

If you are storing multiple different types of object in the same question then using the T parameter MyType does not guarantee that the results you are gonna get will only be MyType results but rather anything that matches the expression will be mapped and returned as MyType. There is not other filtering in the CosmosDB SDK that guarantees that the querying will only occur on objects of that type because CosmosDB has no context of that.

The only reliable way you can do this using the ReplaceDocumentAsync method is to (as Alex AIT said) add the Id property with a [JsonProperty("id")] attribute on your object. Considering that it is a DTO and not a Domain object, this property should be there and it will really help with other functionality such us delete, etc.

I would highly recommend looking at Cosmonaut which is a CosmosDB ORM which also handles Type based collection sharing and the same querying and updating you are trying to achieve without having to mess with any such logic.

Disclaimer: I am the creator of Cosmonaut

On a side note, you should not use ToList() on an IQueryable created using CreateDocumentQuery but instead use the .AsDocumentQuery() method on it and then use the combination of while(query.HasMoreResults) then query.ExecuteNextAsync() method to make proper use of the paginated CosmosDB functionality.

Nick Chapsas
  • 6,872
  • 1
  • 20
  • 29