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

Commit

Permalink
We have docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Elfocrash committed Dec 15, 2018
1 parent ff4a79b commit 07497a4
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 6 deletions.
40 changes: 39 additions & 1 deletion docs/Collection-sharing.md
Original file line number Diff line number Diff line change
@@ -1 +1,39 @@
# Collection sharing
# Collection sharing

## What is collection sharing?

When development on Cosmonaut started there was no option to provision RUs on the database level. Later this feature came in and it has a 50k RUs minimum. It was later reduced to 10k and now it's on 400 RUs for the whole database. That's fine and all but having collection level throughput is still to me the best way to go. It limits the scalability to a single collection.

Collection sharing is the consept of having multiple different types of objects sharing the same collection while Cosmonaut is able to operate on them as if they were in completely different containers.

The benefit of such a feature is that you don't need a single collection per entity type. You can simply have them sharing. If you are also good with your partitioning strategy you will be able to have multiple shared collections with different partition key definitions that make sense and provide optimal read and write performance.

## How can I use Collection sharing?

In order to enable collection sharing you all you have to do is have your POCO implement the `ISharedCosmosEntity` interface and decorate the class with the `SharedCosmosCollectionAttribute`.

An example of an object that is hosted in a shared collection looks like this:

```c#
[SharedCosmosCollection("shared", "somebooks")]
public class Book : ISharedCosmosEntity
{
[JsonProperty("id")]
public string Id { get; set; }

[CosmosPartitionKey]
public string Name { get; set; }

public string CosmosEntityName { get; set; }
}
```

The first parameter at the `SharedCosmosCollection` attribute with value `shared` represents the shared collection name that this object will use. This is the only mandatory parameter for this attribute. The second one with value `somebooks` represents the value that `CosmosEntityName` will automatically be populated with. If you don't set this value then a lowercase pluralised version of the class name will be used instead.

> Note: You do NOT need to set the `CosmosEntityName` value yourself. Leave it as it is and Cosmonaut will do the rest for you.
Even though this is convinient I understand that you might need to have a more dynamic way of specifying the collection that this object should use. 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.
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.
28 changes: 27 additions & 1 deletion docs/Dependency-injection.md
Original file line number Diff line number Diff line change
@@ -1 +1,27 @@
# Dependency Injection
# Dependency Injection

Cosmonaut also has a separate package that adds extensions on top of the .NET Standard Dependency injection framework.

Nuget package: [Cosmonaut.Extensions.Microsoft.DependencyInjection](https://www.nuget.org/packages/Cosmonaut.Extensions.Microsoft.DependencyInjection/)

Installing this package will add a set of methods for `IServiceCollection` called `AddCosmosStore`.

```c#
var cosmosSettings = new CosmosStoreSettings("<<databaseName>>", "<<cosmosUri>>", "<<authkey>>");

serviceCollection.AddCosmosStore<Book>(cosmosSettings);

// or override the collection name
serviceCollection.AddCosmosStore<Book>(cosmosSettings, "myCollection");

//or just by using the Action extension
serviceCollection.AddCosmosStore<Book>("<<databaseName>>", "<<cosmosUri>>", "<<authkey>>", settings =>
{
settings.ConnectionPolicy = connectionPolicy;
settings.DefaultCollectionThroughput = 5000;
settings.IndexingPolicy = new IndexingPolicy(new RangeIndex(DataType.Number, -1),
new RangeIndex(DataType.String, -1));
});
```
74 changes: 73 additions & 1 deletion docs/Document-operations.md
Original file line number Diff line number Diff line change
@@ -1 +1,73 @@
# Working with Entities using Cosmonaut
# Working with Entities using Cosmonaut


## Adding an entity in the CosmosStore
```csharp
var newUser = new User
{
Name = "Nick"
};
var oneAdded = await cosmoStore.AddAsync(newUser);

var multipleAdded = await cosmoStore.AddRangeAsync(manyManyUsers);
```

Using the Range operation allows you to also provide a `Func` that creates a `RequestOptions` for every individual execution that takes place in the operation

## Updating and Upserting entities
When it comes to updating you have two options.

Update example
```c#
var response = await cosmoStore.UpdateAsync(entity);
```

Upsert example
```c#
var response = await cosmoStore.UpsertAsync(entity);
```

The main difference is of course in the functionality.
Update will only update if the item you are updating exists in the database with this id.
Upsert on the other hand will either add the item if there is no item with this id or update it if an item with this id exists.

There are also `Range` variation of both of these methods.

Using one of the Range operations allows you to also provide a `Func` that creates a `RequestOptions` for every individual execution that takes place in the operation. This might be something the Etag in order to ensure that you are updatng the latest version of the document.

Example of an `UpdateRangeAsync` execution that ensures that the latest version of the document is being updated:

```c#
var updated = await booksStore.UpdateRangeAsync(objectsToUpdate, x => new RequestOptions { AccessCondition = new AccessCondition
{
Type = AccessConditionType.IfMatch,
Condition = x.Etag
}});
```

## Removing entities

There are multiple ways to remove an entity in Cosmonaut.

The simplest one is to use any of the overloads of the `RemoveByIdAsync` methods.

```c#
var removedWithId = await cosmoStore.RemoveByIdAsync("documentId");
var removedWithIdAndPartitionKey = await cosmoStore.RemoveByIdAsync("documentId", "partitionKeyValue");
```

There is also the `RemoveAsync` method which uses an entity object to do the removal. However this object needs to have the id property populated and if it's a partitioned, it should also have the partition key value populated.

```c#
var removedEntity = await cosmosStore.RemoveAsync(entity);
```

Last but not least you can use the `RemoveAsync` method that has a predicate in it's signature. This will match all the documents that satistfy the predicate and remove them. You have to keep in mind that this method is doing a cross partition query behind the scenes before it does a direct delete per document. It's not very efficient and it should be used only in rare cases.

```c#
var deleted = await cosmoStore.RemoveAsync(x => x.Name == "Nick");
```

You can specify the `FeedOptions` for the query that takes place, potentially providing a partition key value to limit the scope of the request.

You to also provide a `Func` that creates a `RequestOptions` for every individual execution that takes place in the operation.
26 changes: 25 additions & 1 deletion docs/Logging.md
Original file line number Diff line number Diff line change
@@ -1 +1,25 @@
# Logging
# Logging

## Event source

Cosmonaut uses the .NET Standard's `System.Diagnostics` to log it's actions as dependency events.

By default, this system is deactivated. In order to activated and actually do something with those events you need to create an `EventListener` which will activate the logging and give you the option do something with the logs.

## Cosmonaut.ApplicationInsights

By using this package you are able to log the events as dependencies in [Application Insights](https://azure.microsoft.com/en-gb/services/application-insights/) in detail. The logs are batched and send in intervals OR automatically sent when the batch buffer is filled to max.

Just initialise the AppInsightsTelemetryModule in your Startup or setup pipeline like this.
Example:

```c#
AppInsightsTelemetryModule.Instance.Initialize(new TelemetryConfiguration("InstrumentationKey"));
```

If you already have initialised `TelemetryConfiguration` for your application then use `TelemetryConfiguration.Active` instead of `new TelemetryConfiguration` because if you don't there will be no association between the dependency calls and the parent request.


```c#
AppInsightsTelemetryModule.Instance.Initialize(new TelemetryConfiguration(TelemetryConfiguration.Active));
```
26 changes: 25 additions & 1 deletion docs/Pagination.md
Original file line number Diff line number Diff line change
@@ -1 +1,25 @@
# Pagination
# Pagination

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.

```csharp
var firstPage = await booksStore.Query().WithPagination(1, 10).OrderBy(x=>x.Name).ToListAsync();
var secondPage = await booksStore.Query().WithPagination(2, 10).OrderBy(x => x.Name).ToPagedListAsync();
var thirdPage = await booksStore.Query().WithPagination(secondPage.NextPageToken, 10).OrderBy(x => x.Name).ToPagedListAsync();
var fourthPage = await thirdPage.GetNextPageAsync();
var fifthPage = await booksStore.Query().WithPagination(5, 10).OrderBy(x => x.Name).ToListAsync();
```

`ToListAsync()` on a paged query will just return the results. `ToPagedListAsync()` on the other hand will return a `CosmosPagedResults` object. This object contains the results but also a boolean indicating whether there are more pages after the one you just got but also the continuation token you need to use to get the next page.

## Pagination recommendations

Because page number + page size pagination goes though all the documents until it gets to the requested page, it's potentially slow and expensive.
The recommended approach would be to use the page number + page size approach once for the first page and get the results using the `.ToPagedListAsync()` method. This method will return the next continuation token and it will also tell you if there are more pages for this query. Then use the continuation token alternative of `WithPagination` to continue from your last query.

Keep in mind that this approach means that you have to keep state on the client for the next query, but that's what you'd do if you where using previous/next buttons anyway.
54 changes: 54 additions & 0 deletions docs/Preparing-your-classes-for-the-CosmosStore.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Preparing your classes for the CosmosStore

There are some things you need to get familiar with when it comes to using Cosmonaut. You see, Cosmonaut it doing a lot of things for you behind the scenes but it's performing even better if you do just a little bit of configuration.

## Key attributes

### [CosmosCollection]

The `CosmosCollection` attribute is an optional attribute that you can decorate your entity's class with. It has two purposes.

First it allows you to override the default Cosmonaut collection naming bahaviour, which is to name your collections as a lowercase plularised version of the class name.

Second it allows you to map your class to a pre existing collection with a different name.

> You can of course do a further override of the `CosmosStore` target name at the `CosmosStore` constructor level by providing the `overriddenCollectionName` parameter.
### [SharedCosmosCollection]

In order to enable collection sharing you all you have to do is have your POCO implement the `ISharedCosmosEntity` interface and decorate the class with the `SharedCosmosCollectionAttribute`.

This attribute has one mandatory and one optional parameter namely the `SharedCollectionName` and the `EntityName`.

```c#
[SharedCosmosCollection("shared", "somebooks")]
```

The first parameter at the `SharedCosmosCollection` attribute with value `shared` represents the shared collection name that this object will use. This is the only mandatory parameter for this attribute. The second one with value `somebooks` represents the value that `CosmosEntityName` will automatically be populated with. If you don't set this value then a lowercase pluralised version of the class name will be used instead.

> You can of course do a further override of the `CosmosStore` target name at the `CosmosStore` constructor level by providing the `overriddenCollectionName` parameter.
### [CosmosPartitionKey]

This is a parameterless attribute. It is used to decorate the property that represents the partition key definition of your collection.

```c#
public class Book
{
[CosmosPartitionKey]
public string Name { get; set; }

[JsonProperty("id")]
public string Id { get; set; }
}
```

In this case I have a partition key definition in my collection named `/Name`.

By decorating the `Name` property with the `CosmosPartitionKey` attribute I enable Cosmonaut to do a lot of behind the scenes operation optimisation where it will set the partition key value for you if present, speeding up the operation and making it more cost efficient.

### [JsonProperty("id")]

This is not a Cosmonaut attribute and it is coming from JSON.NET which the underlying Cosmos DB SDK is using.

Even though not required I strongly recommend that you decorate your property which represents the id of the entity with the `[JsonProperty("id")]` attribute. This will prevent any unwanted behaviour with LINQ to SQL conversions and the id property not being mapped propertly.
2 changes: 1 addition & 1 deletion docs/The-CosmosStore.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The choice to go with one or the other is completely up to you and it comes down

In order for an entity to be able to be stored, manipulated and retrieved in a CosmosStore it is required to have a mandatory property. This is the `id` property.

It needs to be a `string` property with name Id (or any capitalisation you want). Even though not neccessary, you should also decorate it with the `[JsonProperty("id")]` attribute. This is neccessary if you want to do any querying based on the id (querying NOT reading). It will also help with unintented behavour when it comes to object mapping and LINQ to SQL transformations.
It needs to be a `string` property with the name either being `Id` (with any capitalisation) or any other name but decorated with the `[JsonProperty("id")]` attribute. Even though not neccessary, when the property is named Id, you should also decorate it with the `[JsonProperty("id")]` attribute. This is neccessary if you want to do any querying based on the id (querying NOT reading). It will also help with unintented behavour when it comes to object mapping and LINQ to SQL transformations.

### CosmosStore's lifetime

Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Welcome to Cosmonaut's documentation!
Getting-started
The-CosmosStore
CosmonautClient
Preparing-your-classes-for-the-CosmosStore

.. _feature-docs:

Expand Down

0 comments on commit 07497a4

Please sign in to comment.