Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fef81d4
Add CouchViewResult
panoukos41 Feb 17, 2021
964c399
Add CouchViewOptions
panoukos41 Feb 17, 2021
9af27df
Add CouchViewRow
panoukos41 Feb 17, 2021
e0419f4
Add GetView methods on ICouchDatabase
panoukos41 Feb 17, 2021
0e9736a
Implement GetView Methods on CouchDatabase
panoukos41 Feb 17, 2021
207b8bb
#112 - Fix object disposed before async method completes
Feb 28, 2021
40d93ff
#92 - Improves FindAsync
matteobortolazzo Mar 1, 2021
53d8087
Rename Type arguments
panoukos41 Mar 3, 2021
9d04d79
#106 - Implement optional discriminator in query builder
matteobortolazzo Mar 3, 2021
653daf2
Refactor View Methods
panoukos41 Mar 3, 2021
7869956
#106 - Implement discriminator in CouchContext
matteobortolazzo Mar 4, 2021
912af75
#113 - Boolean expressions pruning
matteobortolazzo Mar 6, 2021
ed6e123
#113 - Improved pruning
matteobortolazzo Mar 6, 2021
b84434b
Refactor CouchDB View methods
panoukos41 Mar 6, 2021
47758fb
Refactor CouchViewOptions
panoukos41 Mar 6, 2021
ec0468f
Do not default to null for DTOs
panoukos41 Mar 6, 2021
935b4e7
Merge pull request #118 from panoukos41/view_support
matteobortolazzo Mar 7, 2021
79b8edd
#117 - Implement generic key view call
matteobortolazzo Mar 7, 2021
6c5c73d
#117 - Views refactor
matteobortolazzo Mar 8, 2021
cb95cc6
Update Views
matteobortolazzo Mar 9, 2021
ae39fbf
Update README
matteobortolazzo Mar 9, 2021
065ab79
Fix view
matteobortolazzo Mar 9, 2021
e1d3492
README update
matteobortolazzo Mar 9, 2021
4a340d5
Update changelog
matteobortolazzo Mar 9, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
# 2.0.1 (2020-09-19)
# 3.0.0 (2020-03-09)

## Breaking Changes
* Update to [Flurl 3](https://github.com/tmenier/Flurl/releases/tag/Flurl.Http.3.0.0). There should be no differences for the end user, but keep in mind.

## Features
* **Table Splitting**: Ability to use the same database for different document with automatic filtering. ([#106](https://github.com/matteobortolazzo/couchdb-net/issues/106))
* **Views**: Ability to get views. Thanks to [panoukos41](https://github.com/panoukos41)

## Improvements
* **Logical Expressions Prune**: If expressions are constant booleans, they are removed automatically keeping the query valid. ([#113](https://github.com/matteobortolazzo/couchdb-net/issues/113))
* **IsUpAsync**: Returns false on timeout and on not successful codes. ([#107](https://github.com/matteobortolazzo/couchdb-net/issues/107))
* **FindAsync**: Faster when document is not found. ([#92](https://github.com/matteobortolazzo/couchdb-net/issues/92))

# 2.1.0 (2020-09-19)

## Features
* **Indexes"**: Ability to create indexes. ([#102](https://github.com/matteobortolazzo/couchdb-net/issues/102))
Expand Down
17 changes: 10 additions & 7 deletions LATEST_CHANGE.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
## Features
* **Indexes"**: Ability to create indexes. ([#102](https://github.com/matteobortolazzo/couchdb-net/issues/102))
* **Null values"**: New `SetNullValueHandling` method for `CouchOptionsBuilder` to set how to handle null values. ([#101](https://github.com/matteobortolazzo/couchdb-net/issues/101))
* **Query"**: New `Select` and `Convert` methods to select specific fields.
## Breaking Changes
* Update to [Flurl 3](https://github.com/tmenier/Flurl/releases/tag/Flurl.Http.3.0.0). There should be no differences for the end user, but keep in mind.

## Bug Fixes
* **Conflicts**: Fix the query parameter value to get conflicts. ([#100](https://github.com/matteobortolazzo/couchdb-net/issues/100))
* **Query**: Fix queries when variables are used. ([#104](https://github.com/matteobortolazzo/couchdb-net/issues/104))
## Features
* **Table Splitting**: Ability to use the same database for different document with automatic filtering. ([#106](https://github.com/matteobortolazzo/couchdb-net/issues/106))
* **Views**: Ability to get views. Thanks to [panoukos41](https://github.com/panoukos41)

## Improvements
* **Logical Expressions Prune**: If expressions are constant booleans, they are removed automatically keeping the query valid. ([#113](https://github.com/matteobortolazzo/couchdb-net/issues/113))
* **IsUpAsync**: Returns false on timeout and on not successful codes. ([#107](https://github.com/matteobortolazzo/couchdb-net/issues/107))
* **FindAsync**: Faster when document is not found. ([#92](https://github.com/matteobortolazzo/couchdb-net/issues/92))
57 changes: 47 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ The produced Mango JSON:
* [Indexing](#indexing)
* [Index Options](#index-options)
* [Partial Indexes](#partial-indexes)
* [Database Splitting](#database-splitting)
* [Views](#views)
* [Local (non-replicating) Documents](#local-(non-replicating)-documents)
* [Bookmark and Execution stats](#bookmark-and-execution-stats)
* [Users](#users)
Expand Down Expand Up @@ -487,16 +489,7 @@ public class MyDeathStarContext : CouchContext
{
public CouchDatabase<Rebel> Rebels { get; set; }

protected override void OnConfiguring(CouchOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseEndpoint("http://localhost:5984/")
.UseBasicAuthentication("admin", "admin")
// If it finds a index with the same name and ddoc (or null)
// but with different fields and/or sort order,
// it will override the index
.OverrideExistingIndexes();
}
// OnConfiguring(CouchOptionsBuilder optionsBuilder) { ... }

protected override void OnDatabaseCreating(CouchDatabaseBuilder databaseBuilder)
{
Expand All @@ -506,6 +499,48 @@ public class MyDeathStarContext : CouchContext
}
```

## Database Splitting

It is possible to use the same database for multiple types:
```csharp
public class MyDeathStarContext : CouchContext
{
public CouchDatabase<Rebel> Rebels { get; set; }
public CouchDatabase<Vehicle> Vehicles { get; set; }

// OnConfiguring(CouchOptionsBuilder optionsBuilder) { ... }

protected override void OnDatabaseCreating(CouchDatabaseBuilder databaseBuilder)
{
databaseBuilder.Document<Rebel>().ToDatabase("troups");
databaseBuilder.Document<Vehicle>().ToDatabase("troups");
}
}
```
> When multiple `CouchDatabase` point to the same **database**, a `_discriminator` field is added on documents creation.
>
> When querying, a filter by `discriminator` is added automatically.

If you are not using `CouchContext`, you can still use the database slit feature:
```csharp
var rebels = client.GetDatabase<Rebel>("troups", nameof(Rebel));
var vehicles = client.GetDatabase<Vehicle>("troups", nameof(Vehicle));
```

## Views

It's possible to query a view with the following:
```csharp
var options = new CouchViewOptions<string[]>
{
StartKey = new[] {"Luke", "Skywalker"},
IncludeDocs = true
};
var viewRows = await _rebels.GetViewAsync<string[], RebelView>("jedi", "by_name", options);
// OR
var details = await _rebels.GetDetailedViewAsync<int, BattleView>("battle", "by_name", options);
```

## Local (non-replicating) Documents

The Local (non-replicating) document interface allows you to create local documents that are not replicated to other databases.
Expand Down Expand Up @@ -630,3 +665,5 @@ Thanks to [Ben Origas](https://github.com/borigas) for features, ideas and tests
Thanks to [n9](https://github.com/n9) for proxy authentication, some bug fixes, suggestions and the great feedback on the changes feed feature!

Thanks to [Marc](https://github.com/bender-ristone) for NullValueHandling, bug fixes and suggestions!

Thanks to [Panos](https://github.com/panoukos41) for the help with Views and Table splitting.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<NeutralLanguage>en</NeutralLanguage>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
<Version>2.0.0</Version>
<Version>3.0.0</Version>
<PackageIcon>icon.png</PackageIcon>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
</PropertyGroup>
Expand All @@ -32,8 +32,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" PrivateAssets="All" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.6" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" PrivateAssets="All" Version="3.3.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.12" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions src/CouchDB.Driver/ChangesFeed/ChangesFeedStyle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ public class ChangesFeedStyle
/// <summary>
/// The feed will only return the current "winning" revision;
/// </summary>
public static ChangesFeedStyle MainOnly => new ChangesFeedStyle("main_only");
public static ChangesFeedStyle MainOnly => new("main_only");

/// <summary>
/// The feed will return all leaf revisions (including conflicts and deleted former conflicts).
/// </summary>
public static ChangesFeedStyle AllDocs => new ChangesFeedStyle("all_docs");
public static ChangesFeedStyle AllDocs => new("all_docs");

private ChangesFeedStyle(string value)
{
Expand Down
72 changes: 45 additions & 27 deletions src/CouchDB.Driver/CouchClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using CouchDB.Driver.DTOs;
using CouchDB.Driver.Exceptions;
using Newtonsoft.Json;
using System.Net.Http;
using System.Net;
using System.Threading;
using CouchDB.Driver.Options;
Expand All @@ -26,8 +25,10 @@ public partial class CouchClient : ICouchClient
{
private DateTime? _cookieCreationDate;
private string? _cookieToken;
private readonly CouchOptions _options;

[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2213:Disposable fields should be disposed", Justification = "<Pending>")]
private readonly IFlurlClient _flurlClient;
private readonly CouchOptions _options;
private readonly string[] _systemDatabases = { "_users", "_replicator", "_global_changes" };
public Uri Endpoint { get; }

Expand Down Expand Up @@ -107,28 +108,28 @@ private IFlurlClient GetConfiguredClient() =>
#region CRUD

/// <inheritdoc />
public ICouchDatabase<TSource> GetDatabase<TSource>(string database) where TSource : CouchDocument
public ICouchDatabase<TSource> GetDatabase<TSource>(string database, string? discriminator = null) where TSource : CouchDocument
{
CheckDatabaseName(database);
var queryContext = new QueryContext(Endpoint, database);
return new CouchDatabase<TSource>(_flurlClient, _options, queryContext);
return new CouchDatabase<TSource>(_flurlClient, _options, queryContext, discriminator);
}

/// <inheritdoc />
public async Task<ICouchDatabase<TSource>> CreateDatabaseAsync<TSource>(string database,
int? shards = null, int? replicas = null, CancellationToken cancellationToken = default)
int? shards = null, int? replicas = null, string? discriminator = null, CancellationToken cancellationToken = default)
where TSource : CouchDocument
{
QueryContext queryContext = NewQueryContext(database);
HttpResponseMessage response = await CreateDatabaseAsync(queryContext, shards, replicas, cancellationToken)
IFlurlResponse response = await CreateDatabaseAsync(queryContext, shards, replicas, cancellationToken)
.ConfigureAwait(false);

if (response.IsSuccessStatusCode)
if (response.IsSuccessful())
{
return new CouchDatabase<TSource>(_flurlClient, _options, queryContext);
return new CouchDatabase<TSource>(_flurlClient, _options, queryContext, discriminator);
}

if (response.StatusCode == HttpStatusCode.PreconditionFailed)
if (response.StatusCode == (int)HttpStatusCode.PreconditionFailed)
{
throw new CouchException($"Database with name {database} already exists.");
}
Expand All @@ -138,16 +139,16 @@ public async Task<ICouchDatabase<TSource>> CreateDatabaseAsync<TSource>(string d

/// <inheritdoc />
public async Task<ICouchDatabase<TSource>> GetOrCreateDatabaseAsync<TSource>(string database,
int? shards = null, int? replicas = null, CancellationToken cancellationToken = default)
int? shards = null, int? replicas = null, string? discriminator = null, CancellationToken cancellationToken = default)
where TSource : CouchDocument
{
QueryContext queryContext = NewQueryContext(database);
HttpResponseMessage response = await CreateDatabaseAsync(queryContext, shards, replicas, cancellationToken)
IFlurlResponse response = await CreateDatabaseAsync(queryContext, shards, replicas, cancellationToken)
.ConfigureAwait(false);

if (response.IsSuccessStatusCode || response.StatusCode == HttpStatusCode.PreconditionFailed)
if (response.IsSuccessful() || response.StatusCode == (int)HttpStatusCode.PreconditionFailed)
{
return new CouchDatabase<TSource>(_flurlClient, _options, queryContext);
return new CouchDatabase<TSource>(_flurlClient, _options, queryContext, discriminator);
}

throw new CouchException($"Something wrong happened while creating database {database}.");
Expand All @@ -171,7 +172,7 @@ public async Task DeleteDatabaseAsync(string database, CancellationToken cancell
}
}

private Task<HttpResponseMessage> CreateDatabaseAsync(QueryContext queryContext,
private Task<IFlurlResponse> CreateDatabaseAsync(QueryContext queryContext,
int? shards = null, int? replicas = null, CancellationToken cancellationToken = default)
{
IFlurlRequest request = NewRequest()
Expand Down Expand Up @@ -204,17 +205,17 @@ public ICouchDatabase<TSource> GetDatabase<TSource>() where TSource : CouchDocum
}

/// <inheritdoc />
public Task<ICouchDatabase<TSource>> CreateDatabaseAsync<TSource>(int? shards = null, int? replicas = null,
public Task<ICouchDatabase<TSource>> CreateDatabaseAsync<TSource>(int? shards = null, int? replicas = null, string? discriminator = null,
CancellationToken cancellationToken = default) where TSource : CouchDocument
{
return CreateDatabaseAsync<TSource>(GetClassName<TSource>(), shards, replicas, cancellationToken);
return CreateDatabaseAsync<TSource>(GetClassName<TSource>(), shards, replicas, discriminator, cancellationToken);
}

/// <inheritdoc />
public Task<ICouchDatabase<TSource>> GetOrCreateDatabaseAsync<TSource>(int? shards = null, int? replicas = null,
public Task<ICouchDatabase<TSource>> GetOrCreateDatabaseAsync<TSource>(int? shards = null, int? replicas = null, string? discriminator = null,
CancellationToken cancellationToken = default) where TSource : CouchDocument
{
return GetOrCreateDatabaseAsync<TSource>(GetClassName<TSource>(), shards, replicas, cancellationToken);
return GetOrCreateDatabaseAsync<TSource>(GetClassName<TSource>(), shards, replicas, discriminator, cancellationToken);
}

/// <inheritdoc />
Expand Down Expand Up @@ -242,13 +243,13 @@ public ICouchDatabase<TUser> GetUsersDatabase<TUser>() where TUser : CouchUser
/// <inheritdoc />
public Task<ICouchDatabase<CouchUser>> GetOrCreateUsersDatabaseAsync(CancellationToken cancellationToken = default)
{
return GetOrCreateDatabaseAsync<CouchUser>(null, null, cancellationToken);
return GetOrCreateDatabaseAsync<CouchUser>(null, null, null, cancellationToken);
}

/// <inheritdoc />
public Task<ICouchDatabase<TUser>> GetOrCreateUsersDatabaseAsync<TUser>(CancellationToken cancellationToken = default) where TUser : CouchUser
{
return GetOrCreateDatabaseAsync<TUser>(null, null, cancellationToken);
return GetOrCreateDatabaseAsync<TUser>(null, null, null, cancellationToken);
}

#endregion
Expand All @@ -259,12 +260,12 @@ public Task<ICouchDatabase<TUser>> GetOrCreateUsersDatabaseAsync<TUser>(Cancella
public async Task<bool> ExistsAsync(string database, CancellationToken cancellationToken = default)
{
QueryContext queryContext = NewQueryContext(database);
HttpResponseMessage? response = await NewRequest()
IFlurlResponse? response = await NewRequest()
.AllowHttpStatus(HttpStatusCode.NotFound)
.AppendPathSegment(queryContext.EscapedDatabaseName)
.HeadAsync(cancellationToken)
.ConfigureAwait(false);
return response.IsSuccessStatusCode;
return response.IsSuccessful();
}

/// <inheritdoc />
Expand All @@ -273,13 +274,14 @@ public async Task<bool> IsUpAsync(CancellationToken cancellationToken = default)
try
{
StatusResult result = await NewRequest()
.AllowAnyHttpStatus()
.AppendPathSegment("/_up")
.GetJsonAsync<StatusResult>(cancellationToken)
.SendRequestAsync()
.ConfigureAwait(false);
return result.Status == "ok";
return result?.Status == "ok";
}
catch (CouchNotFoundException)
catch (CouchException)
{
return false;
}
Expand Down Expand Up @@ -337,18 +339,34 @@ private void CheckDatabaseName(string database)
/// </summary>
public async ValueTask DisposeAsync()
{
if (_options.AuthenticationType == AuthenticationType.Cookie && _options.LogOutOnDispose)
await DisposeAsync(true).ConfigureAwait(false);
GC.SuppressFinalize(this);
}

protected virtual async Task DisposeAsync(bool disposing)
{
if (disposing && _flurlClient != null)
{
await LogoutAsync().ConfigureAwait(false);
if (_options.AuthenticationType == AuthenticationType.Cookie && _options.LogOutOnDispose)
{
await LogoutAsync().ConfigureAwait(false);
}

_flurlClient.Dispose();
}
_flurlClient.Dispose();
}

#endregion

private string GetClassName<TSource>()
{
Type type = typeof(TSource);
return GetClassName(type);
}

public string GetClassName(Type type)
{
Check.NotNull(type, nameof(type));
return type.GetName(_options);
}
}
Expand Down
Loading