Skip to content

Commit 97f621d

Browse files
grzesiekjaczewskigjaczewskirossmills99
authored
Overload CursorApi.PostCursorAsync to execute ad-hoc queries and return only basic statistics (ArangoDB-Community#488)
* Add a non-generic PostCursorAsync that returns basic statistics * Update CursorApiClient.cs - removed unwanted blank line --------- Co-authored-by: gjaczewski <gjaczewski@actify.com> Co-authored-by: Ross Mills <rossmills99@gmail.com>
1 parent aebfb4e commit 97f621d

File tree

6 files changed

+179
-51
lines changed

6 files changed

+179
-51
lines changed

arangodb-net-standard.Test/CursorApi/CursorApiClientTest.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ namespace ArangoDBNetStandardTest.CursorApi
1818
public class CursorApiClientTest : IClassFixture<CursorApiClientTestFixture>
1919
{
2020
private ICursorApiClient _cursorApi;
21+
private readonly string _testCollection;
2122

2223
public class MyModel
2324
{
@@ -27,6 +28,7 @@ public class MyModel
2728
public CursorApiClientTest(CursorApiClientTestFixture fixture)
2829
{
2930
_cursorApi = fixture.ArangoDBClient.Cursor;
31+
_testCollection = fixture.TestCollection;
3032
}
3133

3234
[Fact]
@@ -351,5 +353,18 @@ public async Task DeleteCursorAsync_ShouldSucceed()
351353

352354
var deleteResponse = await _cursorApi.DeleteCursorAsync(response.Id);
353355
}
356+
357+
[Fact]
358+
public async Task PostCursorAsync_ShouldSucceed_WhenInsertWithBaseCursorResponse()
359+
{
360+
string query = "FOR Name IN [\"Jon\",\"Snow\"] INSERT {Name: Name} INTO @@testCollection";
361+
var bindVars = new Dictionary<string, object> { ["@testCollection"] = _testCollection };
362+
363+
CursorResponseBase response = await _cursorApi.PostCursorAsync(query, bindVars);
364+
365+
Assert.Equal(2, response.Extra.Stats.WritesExecuted);
366+
Assert.Equal(HttpStatusCode.Created, response.Code);
367+
Assert.False(response.Error);
368+
}
354369
}
355370
}

arangodb-net-standard.Test/CursorApi/CursorApiClientTestFixture.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using ArangoDBNetStandard;
2+
using ArangoDBNetStandard.CollectionApi.Models;
3+
using System;
24
using System.Threading.Tasks;
35

46
namespace ArangoDBNetStandardTest.CursorApi
@@ -7,6 +9,8 @@ public class CursorApiClientTestFixture : ApiClientTestFixtureBase
79
{
810
public ArangoDBClient ArangoDBClient { get; private set; }
911

12+
public string TestCollection { get; } = "TestCollection";
13+
1014
public CursorApiClientTestFixture()
1115
{
1216
}
@@ -21,6 +25,20 @@ public override async Task InitializeAsync()
2125

2226
ArangoDBClient = GetArangoDBClient(dbName);
2327
await GetVersionAsync(ArangoDBClient);
28+
29+
try
30+
{
31+
var response = await ArangoDBClient.Collection.PostCollectionAsync(
32+
new PostCollectionBody
33+
{
34+
Name = TestCollection
35+
});
36+
}
37+
catch (ApiErrorException ex) when (ex.ApiError.ErrorNum == 1207)
38+
{
39+
// The collection must exist already, carry on...
40+
Console.WriteLine(ex.Message);
41+
}
2442
}
2543
}
2644
}

arangodb-net-standard/CursorApi/CursorApiClient.cs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,66 @@ public virtual async Task<CursorResponse<T>> PostCursorAsync<T>(
138138
}
139139
}
140140

141+
/// <summary>
142+
/// Execute an AQL query and return basic statistics.
143+
/// </summary>
144+
/// <param name="query"></param>
145+
/// <param name="bindVars"></param>
146+
/// <param name="options"></param>
147+
/// <param name="count"></param>
148+
/// <param name="batchSize"></param>
149+
/// <param name="cache"></param>
150+
/// <param name="memoryLimit"></param>
151+
/// <param name="ttl"></param>
152+
/// <param name="transactionId">Optional. The stream transaction Id.</param>
153+
/// <param name="token">A CancellationToken to observe while waiting for the task to complete or to cancel the task.</param>
154+
/// <returns></returns>
155+
public virtual async Task<CursorResponseBase> PostCursorAsync(
156+
string query,
157+
Dictionary<string, object> bindVars = null,
158+
PostCursorOptions options = null,
159+
bool? count = null,
160+
long? batchSize = null,
161+
bool? cache = null,
162+
long? memoryLimit = null,
163+
int? ttl = null,
164+
string transactionId = null,
165+
CancellationToken token = default)
166+
{
167+
var headerProperties = new CursorHeaderProperties();
168+
if (!string.IsNullOrWhiteSpace(transactionId))
169+
{
170+
headerProperties.TransactionId = transactionId;
171+
}
172+
173+
var postCursorBody = new PostCursorBody
174+
{
175+
Query = query,
176+
BindVars = bindVars,
177+
Options = options,
178+
Count = count,
179+
BatchSize = batchSize,
180+
Cache = cache,
181+
MemoryLimit = memoryLimit,
182+
Ttl = ttl
183+
};
184+
185+
var content = await GetContentAsync(postCursorBody, new ApiClientSerializationOptions(true, true)).ConfigureAwait(false);
186+
var headerCollection = GetHeaderCollection(headerProperties);
187+
using (var response = await _client.PostAsync(_cursorApiPath,
188+
content,
189+
headerCollection,
190+
token).ConfigureAwait(false))
191+
{
192+
if (response.IsSuccessStatusCode)
193+
{
194+
var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
195+
return await DeserializeJsonFromStreamAsync<CursorResponseBase>(stream).ConfigureAwait(false);
196+
}
197+
throw await GetApiErrorExceptionAsync(response).ConfigureAwait(false);
198+
};
199+
}
200+
141201
/// <summary>
142202
/// Deletes an existing cursor and frees the resources associated with it.
143203
/// DELETE /_api/cursor/{cursor-identifier}
@@ -210,4 +270,4 @@ public virtual async Task<CursorResponse<T>> PostAdvanceCursorAsync<T>(string cu
210270
}
211271
}
212272
}
213-
}
273+
}

arangodb-net-standard/CursorApi/ICursorApiClient.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,32 @@ Task<CursorResponse<T>> PostCursorAsync<T>(
5353
CursorHeaderProperties headerProperties = null,
5454
CancellationToken token = default);
5555

56+
/// <summary>
57+
/// Execute an AQL query and return basic statistics.
58+
/// </summary>
59+
/// <param name="query"></param>
60+
/// <param name="bindVars"></param>
61+
/// <param name="options"></param>
62+
/// <param name="count"></param>
63+
/// <param name="batchSize"></param>
64+
/// <param name="cache"></param>
65+
/// <param name="memoryLimit"></param>
66+
/// <param name="ttl"></param>
67+
/// <param name="transactionId">Optional. The stream transaction Id.</param>
68+
/// <param name="token">A CancellationToken to observe while waiting for the task to complete or to cancel the task.</param>
69+
/// <returns></returns>
70+
Task<CursorResponseBase> PostCursorAsync(
71+
string query,
72+
Dictionary<string, object> bindVars = null,
73+
PostCursorOptions options = null,
74+
bool? count = null,
75+
long? batchSize = null,
76+
bool? cache = null,
77+
long? memoryLimit = null,
78+
int? ttl = null,
79+
string transactionId = null,
80+
CancellationToken token = default);
81+
5682
/// <summary>
5783
/// Advances an existing query cursor and gets the next set of results.
5884
/// Replaces <see cref="PutCursorAsync{T}(string, CancellationToken)"/>
Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,16 @@
11
using System.Collections.Generic;
2-
using System.Net;
32

43
namespace ArangoDBNetStandard.CursorApi.Models
54
{
65
/// <summary>
76
/// Response from ArangoDB when creating a new cursor.
87
/// </summary>
98
/// <typeparam name="T"></typeparam>
10-
public class CursorResponse<T> : ICursorResponse<T>
9+
public class CursorResponse<T> : CursorResponseBase, ICursorResponse<T>
1110
{
12-
/// <summary>
13-
/// Indicates whether an error occurred
14-
/// </summary>
15-
/// <remarks>
16-
/// Note that in cases where an error occurs, the ArangoDBNetStandard
17-
/// client will throw an <see cref="ApiErrorException"/> rather than
18-
/// populating this property. A try/catch block should be used instead
19-
/// for any required error handling.
20-
/// </remarks>
21-
public bool Error { get; set; }
22-
23-
/// <summary>
24-
/// the total number of result documents available
25-
/// (only available if the query was executed with the count attribute set)
26-
/// </summary>
27-
public long Count { get; set; }
28-
29-
/// <summary>
30-
/// The HTTP status code
31-
/// </summary>
32-
public HttpStatusCode Code { get; set; }
33-
34-
/// <summary>
35-
/// Optional object with extra information about the query result contained
36-
/// in its <see cref="CursorResponseExtra.Stats"/> sub-attribute.
37-
/// For data-modification queries, the sub-attribute will contain the number of
38-
/// modified documents and the number of documents that could not be modified
39-
/// due to an error (if ignoreErrors query option is specified).
40-
/// </summary>
41-
public CursorResponseExtra Extra { get; set; }
42-
43-
/// <summary>
44-
/// Indicates whether the query result was served from the query cache or not.
45-
/// If the query result is served from the query cache, the extra return attribute
46-
/// will not contain any stats sub-attribute and no profile sub-attribute.
47-
/// </summary>
48-
public bool Cached { get; set; }
49-
50-
/// <summary>
51-
/// Whether there are more results available for the cursor on the server.
52-
/// </summary>
53-
public bool HasMore { get; set; }
54-
5511
/// <summary>
5612
/// Result documents (might be empty if query has no results).
5713
/// </summary>
5814
public IEnumerable<T> Result { get; set; }
59-
60-
/// <summary>
61-
/// ID of temporary cursor created on the server (optional).
62-
/// </summary>
63-
public string Id { get; set; }
6415
}
6516
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System.Net;
2+
3+
namespace ArangoDBNetStandard.CursorApi.Models
4+
{
5+
/// <summary>
6+
/// Base part of the cursor response from ArangoDB not containing a generic result.
7+
/// </summary>
8+
public class CursorResponseBase
9+
{
10+
/// <summary>
11+
/// Indicates whether an error occurred
12+
/// </summary>
13+
/// <remarks>
14+
/// Note that in cases where an error occurs, the ArangoDBNetStandard
15+
/// client will throw an <see cref="ApiErrorException"/> rather than
16+
/// populating this property. A try/catch block should be used instead
17+
/// for any required error handling.
18+
/// </remarks>
19+
public bool Error { get; set; }
20+
21+
/// <summary>
22+
/// the total number of result documents available
23+
/// (only available if the query was executed with the count attribute set)
24+
/// </summary>
25+
public long Count { get; set; }
26+
27+
/// <summary>
28+
/// The HTTP status code
29+
/// </summary>
30+
public HttpStatusCode Code { get; set; }
31+
32+
/// <summary>
33+
/// Optional object with extra information about the query result contained
34+
/// in its <see cref="CursorResponseExtra.Stats"/> sub-attribute.
35+
/// For data-modification queries, the sub-attribute will contain the number of
36+
/// modified documents and the number of documents that could not be modified
37+
/// due to an error (if ignoreErrors query option is specified).
38+
/// </summary>
39+
public CursorResponseExtra Extra { get; set; }
40+
41+
/// <summary>
42+
/// Indicates whether the query result was served from the query cache or not.
43+
/// If the query result is served from the query cache, the extra return attribute
44+
/// will not contain any stats sub-attribute and no profile sub-attribute.
45+
/// </summary>
46+
public bool Cached { get; set; }
47+
48+
/// <summary>
49+
/// Whether there are more results available for the cursor on the server.
50+
/// </summary>
51+
public bool HasMore { get; set; }
52+
53+
/// <summary>
54+
/// ID of temporary cursor created on the server (optional).
55+
/// </summary>
56+
public string Id { get; set; }
57+
}
58+
}

0 commit comments

Comments
 (0)