diff --git a/README.md b/README.md index e1d8013..bbb998f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,169 @@ -# analytics-dotnet-client -The .NET client for Couchbase Enterprise Analytics server +

+ Couchbase +

+ +

Couchbase Analytics .NET SDK

+ +The official .NET SDK for Couchbase Analytics. + +## Install + +Requires .NET 8.0. + +```bash +dotnet add package Couchbase.AnalyticsClient +``` + +## Documentation + +- **Getting Started**: see [`docs/getting-started.md`](docs/getting-started.md) +- **API Reference**: see Official API Reference [here](https://docs.couchbase.com/sdk-api/analytics-dotnet-client-1.0.0/) + +## Quick start + +### Connect + +Create a `Cluster` with a connection string and `Credential`. The connection string supports `http` or `https`, multiple hosts, and query/timeout/TLS parameters. + +```csharp +using Couchbase.AnalyticsClient; +using Couchbase.AnalyticsClient.HTTP; +using Couchbase.AnalyticsClient.Options; + +var credential = Credential.Create("username", "password"); + +var cluster = Cluster.Create( + connectionString: "https://analytics.my-couchbase.example.com:18095?max_retries=5", + credential: credential, + configureOptions: options => options + .WithTimeoutOptions(timeoutOpts => timeoutOpts + .WithQueryTimeout(TimeSpan.FromSeconds(15))) + .WithSecurityOptions(securityOpts => securityOpts + .WithTrustOnlyCapella()) +); +``` + +> [!NOTE] +> Use `http://host:8095` for non-TLS connections, `https://host:18095` for TLS (or your own custom ports for a load balancer or proxy) +> +> If multiple IP addresses are resolved for a host, a connection will be attempted for a random one. If that connection attempt fails, another IP will be picked to attempt a connection, until all are exhausted. +> +> Connection string parameters include: +> - `timeout.connect_timeout`, `timeout.dispatch_timeout`, `timeout.query_timeout` (in milliseconds) +> - `security.trust_only_pem_file`, `security.disable_server_certificate_verification`, `security.cipher_suites` +> - `max_retries` + +### Query + +Run an Analytics statement and stream rows: + +> [!NOTE] +> Results are streamed by default. Use `QueryOptions.WithAsStreaming(false)` to get a blocking result. + +```csharp +using Couchbase.AnalyticsClient.Options; + +var result = await cluster.ExecuteQueryAsync( + "SELECT i from ARRAY_RANGE(1, 100) AS i;", + new QueryOptions() + .WithReadOnly(true) + .WithScanConsistency(QueryScanConsistency.RequestPlus) +).ConfigureAwait(false); + +await foreach (var row in result.ConfigureAwait(false)) +{ + Console.WriteLine(row.ContentAs()); +} +``` + +### Query with parameters + +```csharp +var statement = "SELECT * FROM `travel-sample`.inventory.airline WHERE country = $country LIMIT $limit"; + +var paramResult = await _analytics2Fixture.Cluster.ExecuteQueryAsync( + statement, + new QueryOptions() + .WithNamedParameter("country", "United States") + .WithNamedParameter("limit", 10) +).ConfigureAwait(false); + +await foreach (var row in paramResult.ConfigureAwait(false)) +{ + Console.WriteLine(row.ContentAs()); +} + +/** Output: +{"airline":{"id":"airline_19433","type":"airline","name":"XAIR USA","iata":"XA","icao":"XAU","callsign":"XAIR","country":"United States"}} +... +*/ +``` + +### Database and scope context + +Target a specific database and scope using `Database(...).Scope(...).ExecuteQueryAsync(...)`: + +```csharp +var db = cluster.Database("travel-sample"); +var scope = db.Scope("inventory"); + +var scoped = await scope.ExecuteQueryAsync( + "SELECT id FROM airline LIMIT 5" +).ConfigureAwait(false); + +await foreach (var row in scoped.ConfigureAwait(false)) +{ + Console.WriteLine(row.ContentAs()); +} + +/** Output: +{"id":"airline_19433"} +{"id":"airline_137"} +{"id":"airline_18239"} +{"id":"airline_10123"} +{"id":"airline_19290"} +*/ +``` + +### Options + +> [!WARNING] +> Option classes are immutable records. Each mutation returns a new instance of the options. + +Initialize, or modify options using: + +`With` methods return a new instance of the options, to allow chaining: + +```csharp +var options = new QueryOptions() + .WithReadOnly(true) + .WithScanConsistency(QueryScanConsistency.RequestPlus); +``` + +Or use the initializer syntax: +```csharp +var options = new QueryOptions() +{ + ReadOnly = true, + ScanConsistency = QueryScanConsistency.RequestPlus +} + +// or + +options = options with { + ReadOnly = true, + ScanConsistency = QueryScanConsistency.RequestPlus +} +``` + +### Cleanup + +`Cluster` implements `IDisposable`. Dispose when done to release resources: + +```csharp +cluster.Dispose(); +``` + +## License + +Apache 2.0, see [`LICENSE`](LICENSE). \ No newline at end of file diff --git a/assets/couchbase-filled.png b/assets/couchbase-filled.png new file mode 100644 index 0000000..8a5fc81 Binary files /dev/null and b/assets/couchbase-filled.png differ diff --git a/docs/getting-started.md b/docs/getting-started.md index dbfda0d..d58ae22 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -7,12 +7,12 @@ This guide shows how to install the package, connect to a Couchbase cluster, and Add the package to your project: ```bash -dotnet add package Couchbase.Analytics +dotnet add package Couchbase.AnalyticsClient ``` Requires .NET 8.0. -## Connect +### Connect Create a `Cluster` with a connection string and `Credential`. The connection string supports `http` or `https`, multiple hosts, and query/timeout/TLS parameters. @@ -34,19 +34,22 @@ var cluster = Cluster.Create( ); ``` -Notes: -- Use `http://host:8095` for non-TLS clusters, `https://host:18095` for TLS (or your own custom ports for a load balancer or proxy) -- If multiple IP addresses are resolved for a host, a connection will be attempted for a random IP address. If a connection attempt fails, another IP will be picked to attempt a connection, until all are exhausted. -- Connection string parameters include: - - `timeout.connect_timeout`, `timeout.dispatch_timeout`, `timeout.query_timeout` (in milliseconds) - - `security.trust_only_pem_file`, `security.disable_server_certificate_verification`, `security.cipher_suites` - - `max_retries` +> [!NOTE] +> Use `http://host:8095` for non-TLS connections, `https://host:18095` for TLS (or your own custom ports for a load balancer or proxy) +> +> If multiple IP addresses are resolved for a host, a connection will be attempted for a random one. If that connection attempt fails, another IP will be picked to attempt a connection, until all are exhausted. +> +> Connection string parameters include: +> - `timeout.connect_timeout`, `timeout.dispatch_timeout`, `timeout.query_timeout` (in milliseconds) +> - `security.trust_only_pem_file`, `security.disable_server_certificate_verification`, `security.cipher_suites` +> - `max_retries` -## Query +### Query Run an Analytics statement and stream rows: -Note: Results are streamed by default. Use `QueryOptions.WithAsStreaming(false)` to get a blocking result. +> [!NOTE] +> Results are streamed by default. Use `QueryOptions.WithAsStreaming(false)` to get a blocking result. ```csharp using Couchbase.AnalyticsClient.Options; @@ -56,11 +59,11 @@ var result = await cluster.ExecuteQueryAsync( new QueryOptions() .WithReadOnly(true) .WithScanConsistency(QueryScanConsistency.RequestPlus) -); +).ConfigureAwait(false); -await foreach (var row in result.Rows) +await foreach (var row in result.ConfigureAwait(false)) { - Console.WriteLine(row.ContentAs()); + Console.WriteLine(row.ContentAs()); } ``` @@ -69,12 +72,22 @@ await foreach (var row in result.Rows) ```csharp var statement = "SELECT * FROM `travel-sample`.inventory.airline WHERE country = $country LIMIT $limit"; -var paramResult = await cluster.ExecuteQueryAsync( +var paramResult = await _analytics2Fixture.Cluster.ExecuteQueryAsync( statement, new QueryOptions() .WithNamedParameter("country", "United States") .WithNamedParameter("limit", 10) -); +).ConfigureAwait(false); + +await foreach (var row in paramResult.ConfigureAwait(false)) +{ + Console.WriteLine(row.ContentAs()); +} + +/** Output: +{"airline":{"id":"airline_19433","type":"airline","name":"XAIR USA","iata":"XA","icao":"XAU","callsign":"XAIR","country":"United States"}} +... +*/ ``` ### Database and scope context @@ -86,16 +99,55 @@ var db = cluster.Database("travel-sample"); var scope = db.Scope("inventory"); var scoped = await scope.ExecuteQueryAsync( - "SELECT META().id FROM airline LIMIT 5" -); + "SELECT id FROM airline LIMIT 5" +).ConfigureAwait(false); + +await foreach (var row in scoped.ConfigureAwait(false)) +{ + Console.WriteLine(row.ContentAs()); +} + +/** Output: +{"id":"airline_19433"} +{"id":"airline_137"} +{"id":"airline_18239"} +{"id":"airline_10123"} +{"id":"airline_19290"} +*/ +``` -await foreach (var row in scoped.Rows) +### Options + +> [!WARNING] +> Option classes are immutable records. Each mutation returns a new instance of the options. + +Initialize, or modify options using: + +`With` methods return a new instance of the options, to allow chaining: + +```csharp +var options = new QueryOptions() + .WithReadOnly(true) + .WithScanConsistency(QueryScanConsistency.RequestPlus); +``` + +Or use the initializer syntax: +```csharp +var options = new QueryOptions() { - Console.WriteLine(row.Json.ToString()); + ReadOnly = true, + ScanConsistency = QueryScanConsistency.RequestPlus +} + +// or + +options = options with { + ReadOnly = true, + ScanConsistency = QueryScanConsistency.RequestPlus } ``` -## Cleanup +### Cleanup `Cluster` implements `IDisposable`. Dispose when done to release resources: diff --git a/tests/Couchbase.Analytics.FunctionalTests/NewTest.cs b/tests/Couchbase.Analytics.FunctionalTests/NewTest.cs new file mode 100644 index 0000000..fcfeaed --- /dev/null +++ b/tests/Couchbase.Analytics.FunctionalTests/NewTest.cs @@ -0,0 +1,6 @@ +namespace Couchbase.AnalyticsClient.FunctionalTests; + +public class NewTest +{ + +} \ No newline at end of file