Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overload CursorApi.PostCursorAsync to execute ad-hoc queries and return only basic statistics #488

Merged
merged 2 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions arangodb-net-standard.Test/CursorApi/CursorApiClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace ArangoDBNetStandardTest.CursorApi
public class CursorApiClientTest : IClassFixture<CursorApiClientTestFixture>
{
private ICursorApiClient _cursorApi;
private readonly string _testCollection;

public class MyModel
{
Expand All @@ -27,6 +28,7 @@ public class MyModel
public CursorApiClientTest(CursorApiClientTestFixture fixture)
{
_cursorApi = fixture.ArangoDBClient.Cursor;
_testCollection = fixture.TestCollection;
}

[Fact]
Expand Down Expand Up @@ -351,5 +353,18 @@ public async Task DeleteCursorAsync_ShouldSucceed()

var deleteResponse = await _cursorApi.DeleteCursorAsync(response.Id);
}

[Fact]
public async Task PostCursorAsync_ShouldSucceed_WhenInsertWithBaseCursorResponse()
{
string query = "FOR Name IN [\"Jon\",\"Snow\"] INSERT {Name: Name} INTO @@testCollection";
var bindVars = new Dictionary<string, object> { ["@testCollection"] = _testCollection };

CursorResponseBase response = await _cursorApi.PostCursorAsync(query, bindVars);

Assert.Equal(2, response.Extra.Stats.WritesExecuted);
Assert.Equal(HttpStatusCode.Created, response.Code);
Assert.False(response.Error);
}
}
}
18 changes: 18 additions & 0 deletions arangodb-net-standard.Test/CursorApi/CursorApiClientTestFixture.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using ArangoDBNetStandard;
using ArangoDBNetStandard.CollectionApi.Models;
using System;
using System.Threading.Tasks;

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

public string TestCollection { get; } = "TestCollection";

public CursorApiClientTestFixture()
{
}
Expand All @@ -21,6 +25,20 @@ public override async Task InitializeAsync()

ArangoDBClient = GetArangoDBClient(dbName);
await GetVersionAsync(ArangoDBClient);

try
{
var response = await ArangoDBClient.Collection.PostCollectionAsync(
new PostCollectionBody
{
Name = TestCollection
});
}
catch (ApiErrorException ex) when (ex.ApiError.ErrorNum == 1207)
{
// The collection must exist already, carry on...
Console.WriteLine(ex.Message);
}
}
}
}
62 changes: 61 additions & 1 deletion arangodb-net-standard/CursorApi/CursorApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,66 @@ public virtual async Task<CursorResponse<T>> PostCursorAsync<T>(
}
}

/// <summary>
/// Execute an AQL query and return basic statistics.
/// </summary>
/// <param name="query"></param>
/// <param name="bindVars"></param>
/// <param name="options"></param>
/// <param name="count"></param>
/// <param name="batchSize"></param>
/// <param name="cache"></param>
/// <param name="memoryLimit"></param>
/// <param name="ttl"></param>
/// <param name="transactionId">Optional. The stream transaction Id.</param>
/// <param name="token">A CancellationToken to observe while waiting for the task to complete or to cancel the task.</param>
/// <returns></returns>
public virtual async Task<CursorResponseBase> PostCursorAsync(
string query,
Dictionary<string, object> bindVars = null,
PostCursorOptions options = null,
bool? count = null,
long? batchSize = null,
bool? cache = null,
long? memoryLimit = null,
int? ttl = null,
string transactionId = null,
CancellationToken token = default)
{
var headerProperties = new CursorHeaderProperties();
if (!string.IsNullOrWhiteSpace(transactionId))
{
headerProperties.TransactionId = transactionId;
}

var postCursorBody = new PostCursorBody
{
Query = query,
BindVars = bindVars,
Options = options,
Count = count,
BatchSize = batchSize,
Cache = cache,
MemoryLimit = memoryLimit,
Ttl = ttl
};

var content = await GetContentAsync(postCursorBody, new ApiClientSerializationOptions(true, true)).ConfigureAwait(false);
var headerCollection = GetHeaderCollection(headerProperties);
using (var response = await _client.PostAsync(_cursorApiPath,
content,
headerCollection,
token).ConfigureAwait(false))
{
if (response.IsSuccessStatusCode)
{
var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
return await DeserializeJsonFromStreamAsync<CursorResponseBase>(stream).ConfigureAwait(false);
}
throw await GetApiErrorExceptionAsync(response).ConfigureAwait(false);
};
}

/// <summary>
/// Deletes an existing cursor and frees the resources associated with it.
/// DELETE /_api/cursor/{cursor-identifier}
Expand Down Expand Up @@ -210,4 +270,4 @@ public virtual async Task<CursorResponse<T>> PostAdvanceCursorAsync<T>(string cu
}
}
}
}
}
26 changes: 26 additions & 0 deletions arangodb-net-standard/CursorApi/ICursorApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,32 @@ Task<CursorResponse<T>> PostCursorAsync<T>(
CursorHeaderProperties headerProperties = null,
CancellationToken token = default);

/// <summary>
/// Execute an AQL query and return basic statistics.
/// </summary>
/// <param name="query"></param>
/// <param name="bindVars"></param>
/// <param name="options"></param>
/// <param name="count"></param>
/// <param name="batchSize"></param>
/// <param name="cache"></param>
/// <param name="memoryLimit"></param>
/// <param name="ttl"></param>
/// <param name="transactionId">Optional. The stream transaction Id.</param>
/// <param name="token">A CancellationToken to observe while waiting for the task to complete or to cancel the task.</param>
/// <returns></returns>
Task<CursorResponseBase> PostCursorAsync(
string query,
Dictionary<string, object> bindVars = null,
PostCursorOptions options = null,
bool? count = null,
long? batchSize = null,
bool? cache = null,
long? memoryLimit = null,
int? ttl = null,
string transactionId = null,
CancellationToken token = default);

/// <summary>
/// Advances an existing query cursor and gets the next set of results.
/// Replaces <see cref="PutCursorAsync{T}(string, CancellationToken)"/>
Expand Down
51 changes: 1 addition & 50 deletions arangodb-net-standard/CursorApi/Models/CursorResponse.cs
Original file line number Diff line number Diff line change
@@ -1,65 +1,16 @@
using System.Collections.Generic;
using System.Net;

namespace ArangoDBNetStandard.CursorApi.Models
{
/// <summary>
/// Response from ArangoDB when creating a new cursor.
/// </summary>
/// <typeparam name="T"></typeparam>
public class CursorResponse<T> : ICursorResponse<T>
public class CursorResponse<T> : CursorResponseBase, ICursorResponse<T>
{
/// <summary>
/// Indicates whether an error occurred
/// </summary>
/// <remarks>
/// Note that in cases where an error occurs, the ArangoDBNetStandard
/// client will throw an <see cref="ApiErrorException"/> rather than
/// populating this property. A try/catch block should be used instead
/// for any required error handling.
/// </remarks>
public bool Error { get; set; }

/// <summary>
/// the total number of result documents available
/// (only available if the query was executed with the count attribute set)
/// </summary>
public long Count { get; set; }

/// <summary>
/// The HTTP status code
/// </summary>
public HttpStatusCode Code { get; set; }

/// <summary>
/// Optional object with extra information about the query result contained
/// in its <see cref="CursorResponseExtra.Stats"/> sub-attribute.
/// For data-modification queries, the sub-attribute will contain the number of
/// modified documents and the number of documents that could not be modified
/// due to an error (if ignoreErrors query option is specified).
/// </summary>
public CursorResponseExtra Extra { get; set; }

/// <summary>
/// Indicates whether the query result was served from the query cache or not.
/// If the query result is served from the query cache, the extra return attribute
/// will not contain any stats sub-attribute and no profile sub-attribute.
/// </summary>
public bool Cached { get; set; }

/// <summary>
/// Whether there are more results available for the cursor on the server.
/// </summary>
public bool HasMore { get; set; }

/// <summary>
/// Result documents (might be empty if query has no results).
/// </summary>
public IEnumerable<T> Result { get; set; }

/// <summary>
/// ID of temporary cursor created on the server (optional).
/// </summary>
public string Id { get; set; }
}
}
58 changes: 58 additions & 0 deletions arangodb-net-standard/CursorApi/Models/CursorResponseBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Net;

namespace ArangoDBNetStandard.CursorApi.Models
{
/// <summary>
/// Base part of the cursor response from ArangoDB not containing a generic result.
/// </summary>
public class CursorResponseBase
{
/// <summary>
/// Indicates whether an error occurred
/// </summary>
/// <remarks>
/// Note that in cases where an error occurs, the ArangoDBNetStandard
/// client will throw an <see cref="ApiErrorException"/> rather than
/// populating this property. A try/catch block should be used instead
/// for any required error handling.
/// </remarks>
public bool Error { get; set; }

/// <summary>
/// the total number of result documents available
/// (only available if the query was executed with the count attribute set)
/// </summary>
public long Count { get; set; }

/// <summary>
/// The HTTP status code
/// </summary>
public HttpStatusCode Code { get; set; }

/// <summary>
/// Optional object with extra information about the query result contained
/// in its <see cref="CursorResponseExtra.Stats"/> sub-attribute.
/// For data-modification queries, the sub-attribute will contain the number of
/// modified documents and the number of documents that could not be modified
/// due to an error (if ignoreErrors query option is specified).
/// </summary>
public CursorResponseExtra Extra { get; set; }

/// <summary>
/// Indicates whether the query result was served from the query cache or not.
/// If the query result is served from the query cache, the extra return attribute
/// will not contain any stats sub-attribute and no profile sub-attribute.
/// </summary>
public bool Cached { get; set; }

/// <summary>
/// Whether there are more results available for the cursor on the server.
/// </summary>
public bool HasMore { get; set; }

/// <summary>
/// ID of temporary cursor created on the server (optional).
/// </summary>
public string Id { get; set; }
}
}