Skip to content

Commit

Permalink
Merge pull request #126 from panoukos41/feature/view_queries
Browse files Browse the repository at this point in the history
The example is correct, I can merge this one
  • Loading branch information
matteobortolazzo authored Mar 17, 2021
2 parents eba7edd + 68f5736 commit af22327
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 0 deletions.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,33 @@ var viewRows = await _rebels.GetViewAsync<string[], RebelView>("jedi", "by_name"
var details = await _rebels.GetDetailedViewAsync<int, BattleView>("battle", "by_name", options);
```

You can also query a view with multiple options to get multiple results:
```csharp
var lukeOptions = new CouchViewOptions<string[]>
{
Key = new[] {"Luke", "Skywalker"},
IncludeDocs = true
};
var yodaOptions = new CouchViewOptions<string[]>
{
Key = new[] {"Yoda"},
IncludeDocs = true
};
var queries = new[]
{
lukeOptions,
yodaOptions
};

var results = await _rebels.GetViewQueryAsync<string[], RebelView>("jedi", "by_name", queries);
var lukeRows = results[0];
var yodaRows = results[1];
// OR
var details = await _rebels.GetDetailedViewQueryAsync<string[], RebelView>("jedi", "by_name", queries);
var lukeDetails = details[0];
var yodaDetails = details[1];
```

## 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
32 changes: 32 additions & 0 deletions src/CouchDB.Driver/CouchDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,38 @@ public Task<CouchViewList<TKey, TValue, TSource>> GetDetailedViewAsync<TKey, TVa
return requestTask.SendRequestAsync();
}

/// <inheritdoc/>
public async Task<List<CouchView<TKey, TValue, TSource>>[]> GetViewQueryAsync<TKey, TValue>(string design, string view,
IList<CouchViewOptions<TKey>> queries, CancellationToken cancellationToken = default)
{
CouchViewList<TKey, TValue, TSource>[] result =
await GetDetailedViewQueryAsync<TKey, TValue>(design, view, queries, cancellationToken)
.ConfigureAwait(false);

return result.Select(x => x.Rows).ToArray();
}

/// <inheritdoc/>
public async Task<CouchViewList<TKey, TValue, TSource>[]> GetDetailedViewQueryAsync<TKey, TValue>(string design, string view,
IList<CouchViewOptions<TKey>> queries, CancellationToken cancellationToken = default)
{
Check.NotNull(design, nameof(design));
Check.NotNull(view, nameof(view));
Check.NotNull(queries, nameof(queries));

IFlurlRequest request = NewRequest()
.AppendPathSegments("_design", design, "_view", view, "queries");

CouchViewQueryResult<TKey, TValue, TSource> result =
await request
.PostJsonAsync(new { queries }, cancellationToken)
.ReceiveJson<CouchViewQueryResult<TKey, TValue, TSource>>()
.SendRequestAsync()
.ConfigureAwait(false);

return result.Results;
}

#endregion

#region Utils
Expand Down
19 changes: 19 additions & 0 deletions src/CouchDB.Driver/DTOs/CouchViewQueryResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using CouchDB.Driver.Types;
using CouchDB.Driver.Views;
using Newtonsoft.Json;
using System.Collections.Generic;

#nullable disable
namespace CouchDB.Driver.DTOs
{
internal class CouchViewQueryResult<TKey, TValue, TDoc>
where TDoc : CouchDocument
{
/// <summary>
/// The results in the same order as the queries.
/// </summary>
[JsonProperty("results")]
public CouchViewList<TKey, TValue, TDoc>[] Results { get; set; }
}
}
#nullable restore
28 changes: 28 additions & 0 deletions src/CouchDB.Driver/ICouchDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,34 @@ Task<List<CouchView<TKey, TValue, TSource>>> GetViewAsync<TKey, TValue>(string d
Task<CouchViewList<TKey, TValue, TSource>> GetDetailedViewAsync<TKey, TValue>(string design, string view,
CouchViewOptions<TKey>? options = null, CancellationToken cancellationToken = default);

/// <summary>
/// Executes the specified view function from the specified design document using
/// the queries endpoint. This returns one result for each query option in the provided sequence.
/// </summary>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <param name="design">The design to use.</param>
/// <param name="view">The view to use.</param>
/// <param name="queries">Multiple query options for the request.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains an array with a list of <see cref="CouchView{TKey, TValue, TSource}"/> for each query.</returns>
Task<List<CouchView<TKey, TValue, TSource>>[]> GetViewQueryAsync<TKey, TValue>(string design, string view,
IList<CouchViewOptions<TKey>> queries, CancellationToken cancellationToken = default);

/// <summary>
/// Executes the specified view function from the specified design document using
/// the queries endpoint. This returns one result for each query option in the provided sequence.
/// </summary>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <param name="design">The design to use.</param>
/// <param name="view">The view to use.</param>
/// <param name="queries">Multiple query options for the request.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains a list with a <see cref="CouchViewList{TKey, TSource, TView}"/> for each query.</returns>
Task<CouchViewList<TKey, TValue, TSource>[]> GetDetailedViewQueryAsync<TKey, TValue>(string design, string view,
IList<CouchViewOptions<TKey>> queries, CancellationToken cancellationToken = default);

/// <summary>
/// Since CouchDB v3, it is deprecated (a no-op).
///
Expand Down
118 changes: 118 additions & 0 deletions tests/CouchDB.Driver.UnitTests/Database_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,124 @@ private static void SetupViewResponse(HttpTest httpTest)
});
}

[Fact]
public async Task GetViewQueryAsync()
{
// Arrange
using var httpTest = new HttpTest();
SetupViewQueryResponse(httpTest);
var options = new CouchViewOptions<string[]>
{
Key = new[] {"Luke", "Skywalker"},
Skip = 10
};
var queries = new[]
{
options,
options
};

// Act
var results = await _rebels.GetViewQueryAsync<string[], RebelView>("jedi", "by_name", queries);

// Assert
Assert.Equal(2, results.Length);

Assert.All(results, result =>
{
var rebel = Assert.Single(result);
Assert.Equal("luke", rebel.Id);
Assert.Equal(new[] { "Luke", "Skywalker" }, rebel.Key);
Assert.Equal(3, rebel.Value.NumberOfBattles);
});
httpTest
.ShouldHaveCalled("http://localhost/rebels/_design/jedi/_view/by_name/queries")
.WithVerb(HttpMethod.Post)
.WithRequestBody(@"{""queries"":[{""key"":[""Luke"",""Skywalker""],""skip"":10},{""key"":[""Luke"",""Skywalker""],""skip"":10}]}");
}

[Fact]
public async Task GetDetailedViewQueryAsync()
{
// Arrange
using var httpTest = new HttpTest();
SetupViewQueryResponse(httpTest);
var options = new CouchViewOptions<string[]>
{
Key = new[] {"Luke", "Skywalker"},
Skip = 10
};
var queries = new[]
{
options,
options
};

// Act
var results = await _rebels.GetDetailedViewQueryAsync<string[], RebelView>("jedi", "by_name", queries);

// Assert
Assert.Equal(2, results.Length);

Assert.All(results, result =>
{
Assert.Equal(10, result.Offset);
Assert.Equal(20, result.TotalRows);
var rebel = Assert.Single(result.Rows);
Assert.Equal("luke", rebel.Id);
Assert.Equal(new[] { "Luke", "Skywalker" }, rebel.Key);
Assert.Equal(3, rebel.Value.NumberOfBattles);
});
httpTest
.ShouldHaveCalled("http://localhost/rebels/_design/jedi/_view/by_name/queries")
.WithVerb(HttpMethod.Post)
.WithRequestBody(@"{""queries"":[{""key"":[""Luke"",""Skywalker""],""skip"":10},{""key"":[""Luke"",""Skywalker""],""skip"":10}]}");
}

private static void SetupViewQueryResponse(HttpTest httpTest)
{
httpTest.RespondWithJson(new
{
Results = new[]
{
new
{
Offset = 10,
Total_Rows = 20,
Rows = new[]
{
new
{
Id = "luke",
Key = new [] {"Luke", "Skywalker"},
Value = new
{
NumberOfBattles = 3
}
}
}
},
new
{
Offset = 10,
Total_Rows = 20,
Rows = new[]
{
new
{
Id = "luke",
Key = new [] {"Luke", "Skywalker"},
Value = new
{
NumberOfBattles = 3
}
}
}
}
}
});
}

#endregion

#region Utils
Expand Down

0 comments on commit af22327

Please sign in to comment.