Skip to content
This repository has been archived by the owner on Jul 12, 2020. It is now read-only.

Commit

Permalink
Merging 2.11.3 to master (#108)
Browse files Browse the repository at this point in the history
* Added entity full name support in shared collections

* Update Collection-sharing.md

* Removed indexing from readme as it's no longer relevant

* Fix typos

Some typos were identified and corrected.

* Removed the NoUI from the emulator installation

* Added db level throughput management and dictionary parameters in the client. Addresses #67 and #66

* Update installcosmosemu.ps1

* Added docs for the new CosmosStoreSettings properties

* Update azure-pipelines.yml

* Adds configurable ctor initlisation for infrastracture.

* Added Azure pipelines env variable endpoint

* Fixed the db offer test

* Update The-CosmosStore.md

* Updated to Microsoft.Azure.DocumentDB.Core 2.4.0

* Added ability to configure unique keys for collection (#91)

* Added ability to configure unique keys for collection

* Fixed failing test

* Removed throwing of exception

* Removed optional ctor parameters

* Fixed indentation on CosmosStoreSettings_CreatesCollectionWithUniqueKeyPolicy

* Added a downloads icon in the readme

* Upgraded to SDK 2.4.1 and fixed the Serializer bug

* Use request options serializer when supplied

* Fixes an issue with the DocumentClient serializer

* SDK updated to 2.5.1

* Fixes typos in Read-and-Querying.md (#100)

* Fix #103 (#104)

* Fix #103

* Fixed the "aa" my bad!

* Added unit test

* Fixed the unit tests

* Update README.md

* Added a CosmonautClientFactory and made the DocumentClientFactory public
  • Loading branch information
Elfocrash authored Aug 7, 2019
1 parent 4a206ab commit 2619335
Show file tree
Hide file tree
Showing 46 changed files with 683 additions and 203 deletions.
2 changes: 1 addition & 1 deletion Cosmonaut.sln
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cosmonaut.AzureFunction", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cosmonaut.WebJobs.Extensions", "src\Cosmonaut.WebJobs.Extensions\Cosmonaut.WebJobs.Extensions.csproj", "{30FB4E88-D787-4133-9E56-5C51463BDEC0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cosmonaut.Shared", "samples\Cosmonaut.Shared\Cosmonaut.Shared.csproj", "{0819D70F-20C6-420C-8306-885B530E50E3}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cosmonaut.Shared", "samples\Cosmonaut.Shared\Cosmonaut.Shared.csproj", "{0819D70F-20C6-420C-8306-885B530E50E3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
69 changes: 14 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![Build Status](https://dev.azure.com/nickchapsas/Cosmonaut/_apis/build/status/Elfocrash.Cosmonaut)](https://dev.azure.com/nickchapsas/Cosmonaut/_build/latest?definitionId=2) [![NuGet Package](https://img.shields.io/nuget/v/Cosmonaut.svg)](https://www.nuget.org/packages/Cosmonaut) [![Documentation Status](https://readthedocs.org/projects/cosmonaut/badge/?version=latest)](https://cosmonaut.readthedocs.io/en/latest/?badge=latest) [![Licensed under the MIT License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/Elfocrash/Cosmonaut/blob/master/LICENSE)
[![Build Status](https://dev.azure.com/nickchapsas/Cosmonaut/_apis/build/status/Elfocrash.Cosmonaut)](https://dev.azure.com/nickchapsas/Cosmonaut/_build/latest?definitionId=2) [![NuGet Package](https://img.shields.io/nuget/v/Cosmonaut.svg)](https://www.nuget.org/packages/Cosmonaut) [![NuGet](https://img.shields.io/nuget/dt/cosmonaut.svg)](https://www.nuget.org/packages/cosmonaut) [![Documentation Status](https://readthedocs.org/projects/cosmonaut/badge/?version=latest)](https://cosmonaut.readthedocs.io/en/latest/?badge=latest) [![Licensed under the MIT License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/Elfocrash/Cosmonaut/blob/master/LICENSE)

# Cosmonaut

Expand Down Expand Up @@ -66,7 +66,7 @@ var user = await cosmosStore.FindAsync("userId", "partitionKey");
var user = await cosmosStore.FindAsync("userId", new RequestOptions());
```

##### Quering for entities using LINQ
##### Querying for entities using LINQ

In order to query for entities all you have to do is call the `.Query()` method and then use LINQ to create the query you want.
It is HIGHLY recommended that you use one of the `Async` extension methods to get the results back, such as `ToListAsync` or `FirstOrDefaultAsync` , when available.
Expand All @@ -76,7 +76,7 @@ var user = await cosmoStore.Query().FirstOrDefaultAsync(x => x.Username == "elfo
var users = await cosmoStore.Query().Where(x => x.HairColor == HairColor.Black).ToListAsync(cancellationToken);
```

##### Quering for entities using SQL
##### Querying for entities using SQL

```csharp
// plain sql query
Expand Down Expand Up @@ -104,7 +104,7 @@ Well it's actually pretty simple. Just implement the `ISharedCosmosEntity` inter
The attribute accepts two properties, `SharedCollectionName` which is mandatory and `EntityName` which is optional.
The `SharedCollectionName` property will be used to name the collection that the entity will share with other entities.

The `EntityName` will be used to make the object identifiable for Cosmosnaut. Be default it will pluralize the name of the class, but you can specify it to override this behavior. You can override this by providing your own name by setting the `EntityName` value at the attribute level.
The `EntityName` will be used to make the object identifiable for Cosmosnaut. By default it will pluralize the name of the class, but you can specify it to override this behavior. You can override this by providing your own name by setting the `EntityName` value at the attribute level.

Once you set this up you can add individual CosmosStores with shared collections.

Expand All @@ -130,7 +130,7 @@ public class Car : ISharedCosmosEntity
}
```

Even though this is convinient I understand that you might need to have a dynamic way of setting this.
Even though this is convenient I understand that you might need to have a dynamic way of setting this.
That's why the `CosmosStore` class has some extra constructors that allow you to specify the `overriddenCollectionName` property. This property will override any collection name specified at the attribute level and will use that one instead.

Note: If you have specified a `CollectionPrefix` at the `CosmosStoreSettings` level it will still be added. You are only overriding the collection name that the attribute would normally set.
Expand All @@ -152,7 +152,7 @@ CosmosStore initialisation:
var cosmosStore = new CosmosStore<Car>(someSettings, "oldcars");
```

The outcome of this would be a collection named `oldcars` becase the `shared` collection name is overriden in the constructor.
The outcome of this would be a collection named `oldcars` because the `shared` collection name is overridden in the constructor.
There are also method overloads for the same property at the dependency injection extension level.

#### Pagination
Expand All @@ -162,7 +162,7 @@ Cosmonaut supports two types of pagination.
* Page number + Page size
* ContinuationToken + Page size

Both of there methods work by adding the `.WithPagination()` method after you used any of the `Query` methods.
Both of these methods work by adding the `.WithPagination()` method after you used any of the `Query` methods.

```csharp
var firstPage = await booksStore.Query().WithPagination(1, 10).OrderBy(x=>x.Name).ToListAsync();
Expand Down Expand Up @@ -233,7 +233,7 @@ It is **HIGHLY RECOMMENDED** that you decorate your Id property with the `[JsonP

#### CosmonautClient

Cosmonaut has it's own version of a `DocumentClient` called `CosmonautClient`. The difference is that the `CosmonautClient` interface is more user friendly and it looks more like something you would use in a real life scenario. It won't throw not found exceptions if an item is not found but it will return `null` instead. It will also retry automatically when you get 429s (too many requests).
Cosmonaut has its own version of a `DocumentClient` called `CosmonautClient`. The difference is that the `CosmonautClient` interface is more user friendly and it looks more like something you would use in a real life scenario. It won't throw not found exceptions if an item is not found but it will return `null` instead. It will also retry automatically when you get 429s (too many requests).

It also has support for logging and monitoring as you are going to see in the logging section of this page.

Expand All @@ -243,53 +243,6 @@ There is currently no way to reliably do transactions with the current CosmosDB

Every operational call (Add, Update, Upsert, Delete) however returns it's status back alongside the reason it failed, if it failed, and the entity so you can add your own retry logic.

#### Indexing
By default CosmosDB is created with the following indexing rules

```javascript
{
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [
{
"path": "/*",
"indexes": [
{
"kind": "Range",
"dataType": "Number",
"precision": -1
},
{
"kind": "Range",
"dataType": "String",
"precision": -1
}
{
"kind": "Spatial",
"dataType": "Point"
}
]
}
],
"excludedPaths": []
}
```

Indexing in necessary for things like querying the collections.
Keep in mind that when you manage indexing policy, you can make fine-grained trade-offs between index storage overhead, write and query throughput, and query consistency.

For example if the String datatype is Hash then exact matches like the following,
`cosmoStore.Query().FirstOrDefaultAsync(x => x.SomeProperty.Equals($"Nick Chapsas")`
will return the item if it exists in CosmosDB but
`cosmoStore.Query().FirstOrDefaultAsync(x => x.SomeProperty.StartsWith($"Nick Ch")`
will throw an error. Changing the Hash to Range will work.

However you can get around that by setting the `FeedOptions.EnableScanInQuery` to `true` for this `Query()`

Same goes for ordering. If you use `OrderBy` on a string property you need to have this property's path set up as `Range` and precision `-1`.

More about CosmosDB Indexing [here](https://docs.microsoft.com/en-us/azure/cosmos-db/indexing-policies)

#### Partitioning
Cosmonaut supports partitions out of the box. You can specify which property you want to be your Partition Key by adding the `[CosmosPartitionKey]` attribute above it.

Expand All @@ -308,6 +261,12 @@ Partitions are great but you should these 3 very important things about them and

More on the third issue here [Unique keys in Azure Cosmos DB](https://docs.microsoft.com/en-us/azure/cosmos-db/unique-keys)

#### Optimizing for performance

Cosmonaut by default will create one `CosmonautClient` (which is really a wrapper around the `DocumentClient`) per `CosmosStore`. The logic behind that decision was that each `CosmosStore` might have different configuration from another even on the client level. However in scenarios where you have tens of CosmosStores this can cause socket starvation. The recommendation in such scenarios is to either reuse the same `CosmonautClient` or to cache the CosmosStores internally and swap them around for different CosmosStores. You can see this issue where a multi tenant scenario is discussed and resolved by the use of a client cache.

It is also a good idea in general to create a `CosmonautClient` outside of the CosmosStore logic and reuse the `CosmonautClient` instead of creating one each time if the configuration for the client is the same.

### Logging

#### Event source
Expand Down
13 changes: 0 additions & 13 deletions appveyor.yml

This file was deleted.

5 changes: 3 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ variables:
buildConfiguration: 'Release'

steps:
- powershell: .\installcosmosemu.ps1
- task: azure-cosmosdb.emulator-public-preview.run-cosmosdbemulatorcontainer.CosmosDbEmulator@2
displayName: 'Run Azure Cosmos DB Emulator container'
- script: dotnet build --configuration $(buildConfiguration)
displayName: 'dotnet build $(buildConfiguration)'
- script: dotnet test tests\Cosmonaut.Unit
displayName: 'Running unit tests'
- script: dotnet test tests\Cosmonaut.System
displayName: 'Running integration tests'

env: { 'CosmosDBEndpoint': "$(CosmosDbEmulator.Endpoint)" }
2 changes: 2 additions & 0 deletions docs/Collection-sharing.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ Even though this is convinient I understand that you might need to have a more d
> Note: If you have specified a CollectionPrefix at the CosmosStoreSettings level it will still be added. You are only overriding the collection name that the attribute would normally set.
If you want your shared collection to be partitioned then make sure than the partition key definition is the same in all the objects that are hosted in this collection.

You can also use the `SharedCosmosCollection` constructor overload that uses the `UseEntityFullName` boolean. By using that constructor Cosmonaut will automatically assign the full namespace of the entity as the discriminator value.
16 changes: 8 additions & 8 deletions docs/Reading-and-Querying.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ The CosmonautClient also offers a few new methods in order to make querying and
* `QueryDocumentsAsync<T>(string databaseId, string collectionId,
Expression<Func<Document, bool>> predicate = null, FeedOptions?, CancellationToken?)` - Queries all the documents that match the predicate providies. If the predicate is null then it queries all the documents in the collection. Returns results mapped to generic type `T`.
* `QueryDocumentsAsync(string databaseId, string collectionId,
Expression<Func<Document, bool>> predicate = null, FeedOptions?, CancellationToken?)` - Queries all the documents that match the predicate providies. If the predicate is null then it queries all the documents in the collection. Returns `Document` results.
Expression<Func<Document, bool>> predicate = null, FeedOptions?, CancellationToken?)` - Queries all the documents that match the predicate provides. If the predicate is null then it queries all the documents in the collection. Returns `Document` results.
* `QueryDocumentsAsync<T>(databaseId, collectionId,
string sql, object parameters = null, FeedOptions?, CancellationToken?)` - Similar to QueryMultipleAsync<T>

Expand All @@ -116,12 +116,12 @@ The CosmonautClient also offers a few new methods in order to make querying and
Cosmonaut has a set of extension methods that can be used in both LINQ and SQL based IQueryables in order to asynchronously query the containers.


* `ToListAsync<T>` - Asynchronously queries Cosmos DB and retuns a list of all the data matching the query.
* `ToPagedListAsync<T>` - Asynchronously queries Cosmos DB and retuns a `CosmosPagedResults` response containing a list of all the data matching the query and also whether there are more pages after this query and a continuation token for the next page.
* `FirstAsync<T>` - Asynchronously queries Cosmos DB and retuns the first item matching this query. Throws exception if no items are matched.
* `ToListAsync<T>` - Asynchronously queries Cosmos DB and returns a list of all the data matching the query.
* `ToPagedListAsync<T>` - Asynchronously queries Cosmos DB and returns a `CosmosPagedResults` response containing a list of all the data matching the query and also whether there are more pages after this query and a continuation token for the next page.
* `FirstAsync<T>` - Asynchronously queries Cosmos DB and returns the first item matching this query. Throws exception if no items are matched.
* `FirstOrDefaultAsync<T>` - Asynchronously queries Cosmos DB and retuns the first item matching this query. Returns null if no items are matched.
* `SingleAsync<T>` - Asynchronously queries Cosmos DB and retuns a single item matching this query. Throws exception if no items or more than one item are matched.
* `SingleOrDefaultAsync<T>` - Asynchronously queries Cosmos DB and retuns a single item matching this query. Throws exception if more than one item are matched and returns null if no items are matched.
* `SingleAsync<T>` - Asynchronously queries Cosmos DB and returns a single item matching this query. Throws exception if no items or more than one item are matched.
* `SingleOrDefaultAsync<T>` - Asynchronously queries Cosmos DB and returns a single item matching this query. Throws exception if more than one item are matched and returns null if no items are matched.
* `CountAsync<T>` - Asynchronously queries Cosmos DB and retuns the count of items matching this query.
* `MaxAsync<T>` - Asynchronously queries Cosmos DB and retuns the maximum value of item items matched.
* `MinAsync<T>` - Asynchronously queries Cosmos DB and retuns the minimum value of item items matched.
* `MaxAsync<T>` - Asynchronously queries Cosmos DB and retuns the maximum value of item matched.
* `MinAsync<T>` - Asynchronously queries Cosmos DB and retuns the minimum value of item matched.
21 changes: 13 additions & 8 deletions docs/The-CosmosStore.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,18 @@ The `CosmosStoreSettings` object can be initialised requires 3 parameters in ord

There are other optional settings you can provide such as:

* ConnectionPolicy - The connection policy for this CosmosStore
* ConsistencyLevel - The level of consistency for this CosmosStore
* IndexingPolicy - The indexing policy for this CosmosStore if it's collection in not yet created
* DefaultCollectionThroughput - The default throughput for this CosmosStore if it's collection in not yet created
* JsonSerializerSettings - The object to json serialization settings
* InfiniteRetries - Whether you want infinite retries on throttled requests
* CollectionPrefix - A prefix prepended on the collection name
* `ConnectionPolicy` - The connection policy for this CosmosStore.
* `ConsistencyLevel` - The level of consistency for this CosmosStore.
* `IndexingPolicy` - The indexing policy for this CosmosStore if it's collection in not yet created.
* `DefaultDatabaseThroughput` - The default database level throughput. No database throughput by default.
* `OnDatabaseThroughput` - The action to be taken when the collection that is about to be created is part of a database that has RU/s provisioned for it. `UseDatabaseThroughput` will ignore the `DefaultCollectionThroughput` and use the database's RUs. `DedicateCollectionThroughput` will provision dedicated RUs for the collection of top of the database throughput with the value of `DefaultCollectionThroughput`.
* `DefaultCollectionThroughput` - The default throughput for this CosmosStore if it's collection in not yet created.
* `JsonSerializerSettings` - The object to json serialization settings.
* `InfiniteRetries` - Whether you want infinite retries on throttled requests.
* `CollectionPrefix` - A prefix prepended on the collection name.
* `ProvisionInfrastructureIfMissing` - Whether the `CosmosStore` will automatically provision the infrastructure when the `CosmosStore` is instantiated. Default `true`.

> Note: In some scenarios, especially with .NET Framework apps, you might notice that inintialisation of the `CosmosStore` can cause a deadlock. This is due to it's call from the UI thread and a synchronisation context issue. To work around that, you can simply set the `ProvisionInfrastructureIfMissing` to `false` and then use the `CosmosStore`'s `EnsureInfrastructureProvisionedAsync` method awaited properly.
### CosmosResponse and response handling

Expand Down Expand Up @@ -79,4 +84,4 @@ The CosmosOperationStatus operation status can have one of 5 values.

### Notes

The CosmosStore also exposes the underlying CosmonautClient that it's using to perform operations so you can use that for any other operations you want to make against Cosmos DB. You need to know though that the CosmosStore's context is only limited for it's own methods. Once you use the CosmonautClient or the DocumentClient you are outside of the limitations of CosmosStore so be careful.
The CosmosStore also exposes the underlying CosmonautClient that it's using to perform operations so you can use that for any other operations you want to make against Cosmos DB. You need to know though that the CosmosStore's context is only limited for it's own methods. Once you use the CosmonautClient or the DocumentClient you are outside of the limitations of CosmosStore so be careful.
Loading

0 comments on commit 2619335

Please sign in to comment.