Skip to content

Commit 2c13cc2

Browse files
Merge pull request #105 from matteobortolazzo/dev
v2.1.0
2 parents c5f4e5d + e005d68 commit 2c13cc2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1406
-119
lines changed

LATEST_CHANGE.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
## Features
2-
* **Users"**: New `ChangeUserPassword` mathod for `ICouchDatabase<CouchUser>`.
2+
* **Indexes"**: Ability to create indexes. ([#102](https://github.com/matteobortolazzo/couchdb-net/issues/102))
3+
* **Null values"**: New `SetNullValueHandling` method for `CouchOptionsBuilder` to set how to handle null values. ([#101](https://github.com/matteobortolazzo/couchdb-net/issues/101))
4+
* **Query"**: New `Select` and `Convert` methods to select specific fields.
35

46
## Bug Fixes
5-
* **IsMatch**: Back to public instead of internal;
6-
* **AddOrUpdate**: Added `Async` postfix.
7+
* **Conflicts**: Fix the query parameter value to get conflicts. ([#100](https://github.com/matteobortolazzo/couchdb-net/issues/100))
8+
* **Query**: Fix queries when variables are used. ([#104](https://github.com/matteobortolazzo/couchdb-net/issues/104))

README.md

+86-4
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ var skywalkers = await context.Rebels
3737
.OrderByDescending(r => r.Name)
3838
.ThenByDescending(r => r.Age)
3939
.Take(2)
40-
.Select(r => new {
41-
r.Name,
42-
r.Age
40+
.Select(
41+
r => r.Name,
42+
r => r.Age
4343
})
4444
.ToListAsync();
4545
```
@@ -101,6 +101,9 @@ The produced Mango JSON:
101101
* [DB Changes Feed](#db-changes-feed)
102102
* [Feed Options](#feed-options)
103103
* [Feed Filter](#feed-filter)
104+
* [Indexing](#indexing)
105+
* [Index Options](#index-options)
106+
* [Partial Indexes](#partial-indexes)
104107
* [Local (non-replicating) Documents](#local-(non-replicating)-documents)
105108
* [Bookmark and Execution stats](#bookmark-and-execution-stats)
106109
* [Users](#users)
@@ -192,7 +195,8 @@ If the Where method is not called in the expression, it will at an empty selecto
192195
| sort | OrderBy(..).ThenBy() |
193196
| sort | OrderByDescending(..) |
194197
| sort | OrderByDescending(..).ThenByDescending() |
195-
| fields | Select(x => new { }) |
198+
| fields | Select(x => x.Prop1, x => x.Prop2) |
199+
| fields | Convert<SourceType, SimplerType>() |
196200
| use_index | UseIndex("design_document") |
197201
| use_index | UseIndex(new [] { "design_document", "index_name" }) |
198202
| r | WithReadQuorum(n) |
@@ -331,6 +335,7 @@ var client = new CouchClient("http://localhost:5984", builder => builder
331335
| DisableDocumentPluralization | Disables documents pluralization in requests. |
332336
| SetDocumentCase | Sets the format case for documents. |
333337
| SetPropertyCase | Sets the format case for properties. |
338+
| SetNullValueHandling | Sets how to handle null values. |
334339
| DisableLogOutOnDispose | Disables log out on client dispose. |
335340

336341
- **DocumentCaseTypes**: None, UnderscoreCase *(default)*, DashCase, KebabCase.
@@ -426,6 +431,81 @@ var filter = ChangesFeedFilter.View(view);
426431
ChangesFeedResponse<Rebel> changes = await GetChangesAsync(options: null, filter);
427432
```
428433

434+
## Indexing
435+
436+
It is possible to create indexes to use when querying.
437+
438+
```csharp
439+
// Basic index creation
440+
await _rebels.CreateIndexAsync("rebels_index", b => b
441+
.IndexBy(r => r.Surname))
442+
.ThenBy(r => r.Name));
443+
444+
// Descending index creation
445+
await _rebels.CreateIndexAsync("rebels_index", b => b
446+
.IndexByDescending(r => r.Surname))
447+
.ThenByDescending(r => r.Name));
448+
```
449+
450+
### Index Options
451+
452+
```csharp
453+
// Specifies the design document and/or whether a JSON index is partitioned or global
454+
await _rebels.CreateIndexAsync("rebels_index", b => b
455+
.IndexBy(r => r.Surname),
456+
new IndexOptions()
457+
{
458+
DesignDocument = "surnames_ddoc",
459+
Partitioned = true
460+
});
461+
```
462+
463+
### Partial Indexes
464+
```csharp
465+
// Create an index which excludes documents at index time
466+
await _rebels.CreateIndexAsync("skywalkers_index", b => b
467+
.IndexBy(r => r.Name)
468+
.Where(r => r.Surname == "Skywalker");
469+
```
470+
471+
### Indexes operations
472+
```csharp
473+
// Get the list of indexes
474+
var indexes = await _rebels.GetIndexesAsync();
475+
476+
// Delete an indexes
477+
await _rebels.DeleteIndexAsync(indexes[0]);
478+
// or
479+
await _rebels.DeleteIndexAsync("surnames_ddoc", name: "surnames");
480+
```
481+
482+
### CouchContext Index Configuration
483+
484+
Finally it's possible to configure indexes on the `CouchContext`.
485+
```csharp
486+
public class MyDeathStarContext : CouchContext
487+
{
488+
public CouchDatabase<Rebel> Rebels { get; set; }
489+
490+
protected override void OnConfiguring(CouchOptionsBuilder optionsBuilder)
491+
{
492+
optionsBuilder
493+
.UseEndpoint("http://localhost:5984/")
494+
.UseBasicAuthentication("admin", "admin")
495+
// If it finds a index with the same name and ddoc (or null)
496+
// but with different fields and/or sort order,
497+
// it will override the index
498+
.OverrideExistingIndexes();
499+
}
500+
501+
protected override void OnDatabaseCreating(CouchDatabaseBuilder databaseBuilder)
502+
{
503+
databaseBuilder.Document<Rebel>()
504+
.HasIndex("rebel_surnames_index", b => b.IndexBy(b => b.Surname));
505+
}
506+
}
507+
```
508+
429509
## Local (non-replicating) Documents
430510

431511
The Local (non-replicating) document interface allows you to create local documents that are not replicated to other databases.
@@ -548,3 +628,5 @@ Also, the configurator has `ConfigureFlurlClient` to set custom HTTP client opti
548628
Thanks to [Ben Origas](https://github.com/borigas) for features, ideas and tests like SSL custom validation, multi queryable, async deadlock, cookie authenication and many others.
549629
550630
Thanks to [n9](https://github.com/n9) for proxy authentication, some bug fixes, suggestions and the great feedback on the changes feed feature!
631+
632+
Thanks to [Marc](https://github.com/bender-ristone) for NullValueHandling, bug fixes and suggestions!

src/.editorconfig

+3
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,6 @@ csharp_preserve_single_line_statements = true
147147

148148
# CA1303: Do not pass literals as localized parameters
149149
dotnet_diagnostic.CA1303.severity = silent
150+
151+
# CA1308: Normalize strings to uppercase
152+
dotnet_diagnostic.CA1308.severity = suggestion

src/CouchDB.Driver/ChangesFeed/ChangesFeedFilterExtensions.cs

+6-14
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
using System;
22
using System.IO;
3-
using System.Linq;
43
using System.Linq.Expressions;
54
using System.Net.Http;
65
using System.Threading;
76
using System.Threading.Tasks;
87
using CouchDB.Driver.ChangesFeed.Filters;
98
using CouchDB.Driver.ChangesFeed.Responses;
109
using CouchDB.Driver.Extensions;
11-
using CouchDB.Driver.Options;
1210
using CouchDB.Driver.Query;
1311
using CouchDB.Driver.Types;
1412
using Flurl.Http;
@@ -17,7 +15,7 @@ namespace CouchDB.Driver.ChangesFeed
1715
{
1816
internal static class ChangesFeedFilterExtensions
1917
{
20-
public static async Task<ChangesFeedResponse<TSource>> QueryWithFilterAsync<TSource>(this IFlurlRequest request, CouchOptions options, ChangesFeedFilter filter,
18+
public static async Task<ChangesFeedResponse<TSource>> QueryWithFilterAsync<TSource>(this IFlurlRequest request, IAsyncQueryProvider queryProvider, ChangesFeedFilter filter,
2119
CancellationToken cancellationToken)
2220
where TSource : CouchDocument
2321
{
@@ -32,12 +30,9 @@ public static async Task<ChangesFeedResponse<TSource>> QueryWithFilterAsync<TSou
3230

3331
if (filter is SelectorChangesFeedFilter<TSource> selectorFilter)
3432
{
35-
MethodCallExpression whereExpression = Expression.Call(typeof(Queryable), nameof(Queryable.Where),
36-
new[] { typeof(TSource) }, Expression.Constant(Array.Empty<TSource>().AsQueryable()), selectorFilter.Value);
33+
MethodCallExpression whereExpression = selectorFilter.Value.WrapInWhereExpression();
34+
var jsonSelector = queryProvider.ToString(whereExpression);
3735

38-
var optimizer = new QueryOptimizer();
39-
Expression optimizedQuery = optimizer.Optimize(whereExpression);
40-
var jsonSelector = new QueryTranslator(options).Translate(optimizedQuery);
4136
return await request
4237
.WithHeader("Content-Type", "application/json")
4338
.SetQueryParam("filter", "_selector")
@@ -65,7 +60,7 @@ public static async Task<ChangesFeedResponse<TSource>> QueryWithFilterAsync<TSou
6560
throw new InvalidOperationException($"Filter of type {filter.GetType().Name} not supported.");
6661
}
6762

68-
public static async Task<Stream> QueryContinuousWithFilterAsync<TSource>(this IFlurlRequest request, CouchOptions options, ChangesFeedFilter filter, CancellationToken cancellationToken)
63+
public static async Task<Stream> QueryContinuousWithFilterAsync<TSource>(this IFlurlRequest request, IAsyncQueryProvider queryProvider, ChangesFeedFilter filter, CancellationToken cancellationToken)
6964
where TSource: CouchDocument
7065
{
7166
if (filter is DocumentIdsChangesFeedFilter documentIdsFilter)
@@ -78,12 +73,9 @@ public static async Task<Stream> QueryContinuousWithFilterAsync<TSource>(this IF
7873

7974
if (filter is SelectorChangesFeedFilter<TSource> selectorFilter)
8075
{
81-
MethodCallExpression whereExpression = Expression.Call(typeof(Queryable), nameof(Queryable.Where),
82-
new[] { typeof(TSource) }, Expression.Constant(Array.Empty<TSource>().AsQueryable()), selectorFilter.Value);
76+
MethodCallExpression whereExpression = selectorFilter.Value.WrapInWhereExpression();
77+
var jsonSelector = queryProvider.ToString(whereExpression);
8378

84-
var optimizer = new QueryOptimizer();
85-
Expression optimizedQuery = optimizer.Optimize(whereExpression);
86-
var jsonSelector = new QueryTranslator(options).Translate(optimizedQuery);
8779
return await request
8880
.WithHeader("Content-Type", "application/json")
8981
.SetQueryParam("filter", "_selector")

src/CouchDB.Driver/CouchClient.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ private IFlurlClient GetConfiguredClient() =>
9090
{
9191
s.JsonSerializer = new NewtonsoftJsonSerializer(new JsonSerializerSettings
9292
{
93-
ContractResolver = new CouchContractResolver(_options.PropertiesCase)
93+
ContractResolver = new CouchContractResolver(_options.PropertiesCase),
94+
NullValueHandling = _options.NullValueHandling ?? NullValueHandling.Include
9495
});
9596
s.BeforeCallAsync = OnBeforeCallAsync;
9697
if (_options.ServerCertificateCustomValidationCallback != null)

0 commit comments

Comments
 (0)