diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index c1edf5b94b..f102c1535a 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -49,11 +49,11 @@ jobs: key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.?sproj') }} restore-keys: | ${{ runner.os }}-nuget- - + - run: "./build.sh integrate ${{ matrix.version }} readonly,writable random:test_only_one --report" name: Integration Tests working-directory: client - + - name: Upload test report if: failure() uses: actions/upload-artifact@v3 @@ -62,12 +62,13 @@ jobs: path: client/build/output/* integration-opensearch-unreleased: + if: false # TODO: Temporarily disabled due to failures building & running OpenSearch from source, pending investigation & fixes (https://github.com/opensearch-project/opensearch-net/issues/268) name: Integration OpenSearch Unreleased runs-on: ubuntu-latest strategy: fail-fast: false matrix: - opensearch_ref: + opensearch_ref: - '1.x' - '2.x' - 'main' @@ -77,7 +78,7 @@ jobs: uses: actions/checkout@v3 with: path: client - + - uses: actions/setup-dotnet@v3 with: dotnet-version: | @@ -90,7 +91,7 @@ jobs: key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.?sproj') }} restore-keys: | ${{ runner.os }}-nuget- - + - name: Restore or Build OpenSearch id: opensearch uses: ./client/.github/actions/build-opensearch @@ -99,14 +100,14 @@ jobs: security_plugin: ${{ matrix.opensearch_ref == '1.x' }} knn_plugin: true plugins_output_directory: ${{ env.OPENSEARCH_PLUGINS_DIRECTORY }} - + - run: "./build.sh integrate $OPENSEARCH_VERSION readonly,writable random:test_only_one --report" name: Integration Tests working-directory: client env: OPENSEARCH_VERSION: ${{ steps.opensearch.outputs.version }} OPENSEARCH_DISTRIBUTION: ${{ steps.opensearch.outputs.distribution }} - + - name: Upload test report if: failure() uses: actions/upload-artifact@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bf530af7f..3d23b97bf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Added - Added support for point-in-time search and associated APIs ([#405](https://github.com/opensearch-project/opensearch-net/pull/405)) - Added support for the component template APIs ([#411](https://github.com/opensearch-project/opensearch-net/pull/411)) +- Added support for the composable index template APIs ([#437](https://github.com/opensearch-project/opensearch-net/pull/437)) ### Removed - Removed the `Features` API which is not supported by OpenSearch from the low-level client ([#331](https://github.com/opensearch-project/opensearch-net/pull/331)) @@ -113,4 +114,4 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) [Unreleased]: https://github.com/opensearch-project/opensearch-net/compare/v1.5.0...main [1.5.0]: https://github.com/opensearch-project/opensearch-net/compare/v1.4.0...v1.5.0 [1.4.0]: https://github.com/opensearch-project/opensearch-net/compare/v1.3.0...v1.4.0 -[1.3.0]: https://github.com/opensearch-project/opensearch-net/compare/v1.2.0...v1.3.0 \ No newline at end of file +[1.3.0]: https://github.com/opensearch-project/opensearch-net/compare/v1.2.0...v1.3.0 diff --git a/OpenSearch.sln.DotSettings b/OpenSearch.sln.DotSettings index 0326966f7b..d831b30a7f 100644 --- a/OpenSearch.sln.DotSettings +++ b/OpenSearch.sln.DotSettings @@ -14,33 +14,6 @@ &lt;inspection_tool class="UnnecessaryReturnJS" enabled="false" level="WARNING" enabled_by_default="false" /&gt; &lt;inspection_tool class="WrongPropertyKeyValueDelimiter" enabled="false" level="WEAK WARNING" enabled_by_default="false" /&gt; &lt;/profile&gt;</IDEA_SETTINGS><VBReformatCode>True</VBReformatCode><HtmlReformatCode>True</HtmlReformatCode><CSUpdateFileHeader>True</CSUpdateFileHeader><CSReorderTypeMembers>True</CSReorderTypeMembers><CSReformatCode>True</CSReformatCode></Profile> - /* SPDX-License-Identifier: Apache-2.0 -* -* The OpenSearch Contributors require contributions made to -* this file be licensed under the Apache-2.0 license or a -* compatible open source license. -* -* Modifications Copyright OpenSearch Contributors. See -* GitHub history for details. -* -* Licensed to Elasticsearch B.V. under one or more contributor -* license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright -* ownership. Elasticsearch B.V. licenses this file to you under -* the Apache License, Version 2.0 (the "License"); you may -* not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ - True 56A87048-9065-459B-826D-3DF68B409845/d:Views diff --git a/guides/index-template.md b/guides/index-template.md index 96fe338803..495d873b39 100644 --- a/guides/index-template.md +++ b/guides/index-template.md @@ -1,10 +1,8 @@ # Index Template Index templates allow you to define default settings, mappings, and aliases for one or more indices during their creation. This guide will teach you how to create index templates and apply them to indices using the OpenSearch .NET client. +See [samples/Samples/IndexTemplate/IndexTemplateSample.cs](../samples/Samples/IndexTemplate/IndexTemplateSample.cs) for a complete working sample. ## Setup -**At the time of writing the API methods related to composable templates do not yet exist in the high-level client, as such this guide makes use of their low-level counterparts.** - - Assuming you have OpenSearch running locally on port 9200, you can create a client instance with the following code: ```csharp @@ -19,95 +17,88 @@ var config = new ConnectionSettings(node) var client = new OpenSearchClient(config);; ``` +The below examples are based off of the following class definition to represent the contents of the index: -## Index Template API Actions +```csharp +public class Book +{ + public string? Title { get; set; } + public string? Author { get; set; } + public DateTime? PublishedOn { get; set; } + public int? Pages { get; set; } +} +``` +## Index Template API Actions ### Create an Index Template You can create an index template to define default settings and mappings for indices of certain patterns. The following example creates an index template named `books` with default settings and mappings for indices of the `books-*` pattern: ```csharp -client.LowLevel.Indices.PutTemplateV2ForAll("books", PostData.Serializable(new -{ - index_patterns = new[] { "books-*" }, - priority = 0, - template = new - { - settings = new - { - index = new - { - number_of_shards = 3, - number_of_replicas = 0 - } - }, - mappings = new - { - properties = new - { - title = new { type = "text" }, - author = new { type = "text" }, - published_on = new { type = "date" }, - pages = new { type = "integer" } - } - } - } -})); +var putTemplate = await client.Indices.PutComposableTemplateAsync("books", d => d + .IndexPatterns("books-*") + .Priority(0) + .Template(t => t + .Settings(s => s + .NumberOfShards(3) + .NumberOfReplicas(0)) + .Map(m => m + .Properties(p => p + .Text(f => f.Name(b => b.Title)) + .Text(f => f.Name(b => b.Author)) + .Date(f => f.Name(b => b.PublishedOn)) + .Number(f => f.Name(b => b.Pages).Type(NumberType.Integer)) + )))); +Console.WriteLine($"Put Template: {putTemplate.IsValid}"); +// -> Put Template: True ``` Now, when you create an index that matches the `books-*` pattern, OpenSearch will automatically apply the template's settings and mappings to the index. Let's create an index named `books-nonfiction` and verify that its settings and mappings match those of the template: ```csharp -client.Indices.Create("books-nonfiction"); -var getResponse = client.Indices.Get("books-nonfiction"); -Console.WriteLine(getResponse.Indices["books-nonfiction"].Mappings.Properties["pages"].Type); // integer -``` +var createIndex = await client.Indices.CreateAsync("books-nonfiction"); +Console.WriteLine($"Create Index: {createIndex.IsValid}"); +// -> Create Index: True +var getIndex = await client.Indices.GetAsync("books-nonfiction"); +Console.WriteLine($"`pages` property type: {getIndex.Indices["books-nonfiction"].Mappings.Properties["pages"].Type}"); +// -> `pages` property type: integer +``` ### Multiple Index Templates ```csharp -var createResponseOne = client.LowLevel.Indices.PutTemplateV2ForAll("books", PostData.Serializable(new -{ - index_patterns = new[] { "books-*" }, - priority = 0, - template = new - { - settings = new - { - index = new - { - number_of_shards = 3, - number_of_replicas = 0 - } - } - } -})); - -client.LowLevel.Indices.PutTemplateV2ForAll("books-fiction", PostData.Serializable(new -{ - index_patterns = new[] { "books-fiction-*" }, - priority = 1, // higher priority than the `books` template - template = new - { - settings = new - { - index = new - { - number_of_shards = 1, - number_of_replicas = 1 - } - } - } -})); +putTemplate = await client.Indices.PutComposableTemplateAsync("books", d => d + .IndexPatterns("books-*") + .Priority(0) + .Template(t => t + .Settings(s => s + .NumberOfShards(3) + .NumberOfReplicas(0)))); +Console.WriteLine($"Put Template: {putTemplate.IsValid}"); +// -> Put Template: True + +putTemplate = await client.Indices.PutComposableTemplateAsync("books-fiction", d => d + .IndexPatterns("books-fiction-*") + .Priority(1) // higher priority than the `books` template + .Template(t => t + .Settings(s => s + .NumberOfShards(1) + .NumberOfReplicas(1)))); +Console.WriteLine($"Put Template: {putTemplate.IsValid}"); +// -> Put Template: True ``` When we create an index named `books-fiction-romance`, OpenSearch will apply the `books-fiction-*` template's settings to the index: ```csharp -client.Indices.Create("books-fiction-romance"); -var getResponse = client.Indices.Get("books-fiction-romance"); -Console.WriteLine(getResponse.Indices["books-fiction-romance"].Settings.NumberOfShards); // 1 +createIndex = await client.Indices.CreateAsync("books-fiction-romance"); +Console.WriteLine($"Create Index: {createIndex.IsValid}"); +// -> Create Index: True + +getIndex = await client.Indices.GetAsync("books-fiction-romance"); +Console.WriteLine($"Number of shards: {getIndex.Indices["books-fiction-romance"].Settings.NumberOfShards}"); +// -> Number of shards: 1 ``` @@ -115,82 +106,71 @@ Console.WriteLine(getResponse.Indices["books-fiction-romance"].Settings.NumberOf Composable index templates are a new type of index template that allow you to define multiple component templates and compose them into a final template. The following example creates a component template named `books_mappings` with default mappings for indices of the `books-*` and `books-fiction-*` patterns: ```csharp -// Create a component template -client.Cluster.PutComponentTemplate("books_mappings", ct => ct - .Template(t => t - .Map(m => m - .Properties(p => p - .Text(tp => tp - .Name("title")) - .Text(tp => tp - .Name("author")) - .Date(d => d - .Name("published_on")) - .Number(n => n - .Name("pages") - .Type(NumberType.Integer)))))); - -// Create an index template for "books" -var createBooksTemplateResponse = client.LowLevel.Indices.PutTemplateV2ForAll("books", PostData.Serializable(new -{ - index_patterns = new[] { "books-*" }, - composed_of = new[] { "books_mappings" }, - priority = 0, - template = new - { - settings = new - { - index = new - { - number_of_shards = 3, - number_of_replicas = 0 - } - } - } -})); - -// Create an index template for "books-fiction" -var createBooksFictionTemplateResponse = client.LowLevel.Indices.PutTemplateV2ForAll("books-fiction", PostData.Serializable(new -{ - index_patterns = new[] { "books-fiction-*" }, - composed_of = new[] { "books_mappings" }, - priority = 1, - template = new - { - settings = new - { - index = new - { - number_of_shards = 1, - number_of_replicas = 1 - } - } - } -})); +var putComponentTemplate = await client.Cluster.PutComponentTemplateAsync("books_mappings", d => d + .Template(t => t + .Map(m => m + .Properties(p => p + .Text(f => f.Name(b => b.Title)) + .Text(f => f.Name(b => b.Author)) + .Date(f => f.Name(b => b.PublishedOn)) + .Number(f => f.Name(b => b.Pages).Type(NumberType.Integer)) + )))); +Console.WriteLine($"Put Component Template: {putComponentTemplate.IsValid}"); +// -> Put Component Template: True + +putTemplate = await client.Indices.PutComposableTemplateAsync("books", d => d + .IndexPatterns("books-*") + .Priority(0) + .ComposedOf("books_mappings") + .Template(t => t + .Settings(s => s + .NumberOfShards(3) + .NumberOfReplicas(0)))); +Console.WriteLine($"Put Template: {putTemplate.IsValid}"); +// -> Put Template: True + +putTemplate = await client.Indices.PutComposableTemplateAsync("books-fiction", d => d + .IndexPatterns("books-fiction-*") + .Priority(1) // higher priority than the `books` template + .ComposedOf("books_mappings") + .Template(t => t + .Settings(s => s + .NumberOfShards(1) + .NumberOfReplicas(1)))); +Console.WriteLine($"Put Template: {putTemplate.IsValid}"); +// -> Put Template: True ``` When we create an index named `books-fiction-horror`, OpenSearch will apply the `books-fiction-*` template's settings, and `books_mappings` template mappings to the index: ```csharp -client.Indices.Create("books-fiction-horror"); -var getResponse = client.Indices.Get("books-fiction-horror"); -Console.WriteLine(getResponse.Indices["books-fiction-horror"].Settings.NumberOfShards); // 1 Console.WriteLine(getResponse.Indices["books-fiction-horror"].Mappings.Properties["pages"].Type); // integer +createIndex = await client.Indices.CreateAsync("books-fiction-horror"); +Console.WriteLine($"Create Index: {createIndex.IsValid}"); +// -> Create Index: True + +getIndex = await client.Indices.GetAsync("books-fiction-horror"); +Console.WriteLine($"Number of shards: {getIndex.Indices["books-fiction-horror"].Settings.NumberOfShards}"); +Console.WriteLine($"`pages` property type: {getIndex.Indices["books-fiction-horror"].Mappings.Properties["pages"].Type}"); +// -> Number of shards: 1 +// -> `pages` property type: integer ``` ### Get an Index Template -You can get an index template with the `GetTemplateV2ForAll` API action. The following example gets the `books` index template: +You can get an index template with the `GetComposableTemplate` API action. The following example gets the `books` index template: ```csharp -var getResponse = client.LowLevel.Indices.GetTemplateV2ForAll("books").Body; -Console.WriteLine($"Get response: {getResponse}"); // Get response: {"books":{"order":0,"index_patterns":["books-*"],"settings":{"index":{"number_of_shards":"3","number_of_replicas":"0"}},"mappings":{},"aliases":{}}} +var getTemplate = await client.Indices.GetComposableTemplateAsync("books"); +Console.WriteLine($"First index pattern: {getTemplate.IndexTemplates.First().IndexTemplate.IndexPatterns.First()}"); +// -> First index pattern: books-* ``` ### Delete an Index Template -You can delete an index template with the `DeleteTemplateV2ForAll` API action. The following example deletes the `books` index template: +You can delete an index template with the `DeleteComposableTemplate` API action. The following example deletes the `books` index template: ```csharp -var deleteResponse = client.LowLevel.Indices.DeleteTemplateV2ForAll("books"); -Console.WriteLine($"Delete response: {deleteResponse}"); // Delete response: {"acknowledged":true} +var deleteTemplate = await client.Indices.DeleteComposableTemplateAsync("books"); +Console.WriteLine($"Delete Template: {deleteTemplate.IsValid}"); +// -> Delete Template: True ``` @@ -198,7 +178,12 @@ Console.WriteLine($"Delete response: {deleteResponse}"); // Delete response: {"a Let's delete all resources created in this guide: ```csharp -client.Indices.Delete("books-"); -client.LowLevel.Indices.DeleteTemplateV2ForAll("books-fiction"); -client.Cluster.DeleteComponentTemplate("books_mappings"); +var deleteIndex = await client.Indices.DeleteAsync("books-*"); +Console.WriteLine($"Delete Index: {deleteIndex.IsValid}"); + +deleteTemplate = await client.Indices.DeleteComposableTemplateAsync("books-fiction"); +Console.WriteLine($"Delete Template: {deleteTemplate.IsValid}"); + +var deleteComponentTemplate = await client.Cluster.DeleteComponentTemplateAsync("books_mappings"); +Console.WriteLine($"Delete Component Template: {deleteComponentTemplate.IsValid}"); ``` diff --git a/guides/json.md b/guides/json.md index 8984c53362..b946b59000 100644 --- a/guides/json.md +++ b/guides/json.md @@ -11,7 +11,7 @@ - [PostData.MultiJson](#postdatamultijson) # Making Raw JSON REST Requests -The OpenSearch client implements many high-level REST DSLs that invoke OpenSearch APIs. However you may find yourself in a situation that requires you to invoke an API that is not supported by the client. You can use `client.LowLevel.DoRequest` to do so. See [samples/Samples/RawJsonSample/Program.cs](../samples/Samples/RawJsonSample/Program.cs) for a complete working sample. +The OpenSearch client implements many high-level REST DSLs that invoke OpenSearch APIs. However you may find yourself in a situation that requires you to invoke an API that is not supported by the client. You can use `client.LowLevel.DoRequest` to do so. See [samples/Samples/RawJson/RawJsonSample.cs](../samples/Samples/RawJson/RawJsonSample.cs) for a complete working sample. ## HTTP Methods diff --git a/samples/Samples/IndexTemplate/IndexTemplateSample.cs b/samples/Samples/IndexTemplate/IndexTemplateSample.cs new file mode 100644 index 0000000000..eb89ed1344 --- /dev/null +++ b/samples/Samples/IndexTemplate/IndexTemplateSample.cs @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +*/ + +using System.Diagnostics; +using OpenSearch.Client; + +namespace Samples.IndexTemplate; + +public class IndexTemplateSample : Sample +{ + public IndexTemplateSample() + : base("index-template", "A sample demonstrating how to use the client to create and manage index templates") { } + + protected override async Task Run(IOpenSearchClient client) + { + // Create index template + + var putTemplate = await client.Indices.PutComposableTemplateAsync("books", d => d + .IndexPatterns("books-*") + .Priority(0) + .Template(t => t + .Settings(s => s + .NumberOfShards(3) + .NumberOfReplicas(0)) + .Map(m => m + .Properties(p => p + .Text(f => f.Name(b => b.Title)) + .Text(f => f.Name(b => b.Author)) + .Date(f => f.Name(b => b.PublishedOn)) + .Number(f => f.Name(b => b.Pages).Type(NumberType.Integer)) + )))); + Debug.Assert(putTemplate.IsValid, putTemplate.DebugInformation); + Console.WriteLine($"Put Template: {putTemplate.IsValid}"); + + // Confirm mapping + + var createIndex = await client.Indices.CreateAsync("books-nonfiction"); + Debug.Assert(createIndex.IsValid, createIndex.DebugInformation); + Console.WriteLine($"Create Index: {createIndex.IsValid}"); + + var getIndex = await client.Indices.GetAsync("books-nonfiction"); + Debug.Assert( + getIndex.Indices["books-nonfiction"].Mappings.Properties["pages"].Type == "integer", + "`pages` property should have `integer` type"); + Console.WriteLine($"`pages` property type: {getIndex.Indices["books-nonfiction"].Mappings.Properties["pages"].Type}"); + + // Multiple index templates + + putTemplate = await client.Indices.PutComposableTemplateAsync("books", d => d + .IndexPatterns("books-*") + .Priority(0) + .Template(t => t + .Settings(s => s + .NumberOfShards(3) + .NumberOfReplicas(0)))); + Debug.Assert(putTemplate.IsValid, putTemplate.DebugInformation); + Console.WriteLine($"Put Template: {putTemplate.IsValid}"); + + putTemplate = await client.Indices.PutComposableTemplateAsync("books-fiction", d => d + .IndexPatterns("books-fiction-*") + .Priority(1) // higher priority than the `books` template + .Template(t => t + .Settings(s => s + .NumberOfShards(1) + .NumberOfReplicas(1)))); + Debug.Assert(putTemplate.IsValid, putTemplate.DebugInformation); + Console.WriteLine($"Put Template: {putTemplate.IsValid}"); + + // Validate settings + + createIndex = await client.Indices.CreateAsync("books-fiction-romance"); + Debug.Assert(createIndex.IsValid, createIndex.DebugInformation); + Console.WriteLine($"Create Index: {createIndex.IsValid}"); + + getIndex = await client.Indices.GetAsync("books-fiction-romance"); + Debug.Assert( + getIndex.Indices["books-fiction-romance"].Settings.NumberOfShards == 1, + "`books-fiction-romance` index should have 1 shard"); + Console.WriteLine($"Number of shards: {getIndex.Indices["books-fiction-romance"].Settings.NumberOfShards}"); + + // Component templates + + var putComponentTemplate = await client.Cluster.PutComponentTemplateAsync("books_mappings", d => d + .Template(t => t + .Map(m => m + .Properties(p => p + .Text(f => f.Name(b => b.Title)) + .Text(f => f.Name(b => b.Author)) + .Date(f => f.Name(b => b.PublishedOn)) + .Number(f => f.Name(b => b.Pages).Type(NumberType.Integer)) + )))); + Debug.Assert(putComponentTemplate.IsValid, putComponentTemplate.DebugInformation); + Console.WriteLine($"Put Component Template: {putComponentTemplate.IsValid}"); + + putTemplate = await client.Indices.PutComposableTemplateAsync("books", d => d + .IndexPatterns("books-*") + .Priority(0) + .ComposedOf("books_mappings") + .Template(t => t + .Settings(s => s + .NumberOfShards(3) + .NumberOfReplicas(0)))); + Debug.Assert(putTemplate.IsValid, putTemplate.DebugInformation); + Console.WriteLine($"Put Template: {putTemplate.IsValid}"); + + putTemplate = await client.Indices.PutComposableTemplateAsync("books-fiction", d => d + .IndexPatterns("books-fiction-*") + .Priority(1) // higher priority than the `books` template + .ComposedOf("books_mappings") + .Template(t => t + .Settings(s => s + .NumberOfShards(1) + .NumberOfReplicas(1)))); + Debug.Assert(putTemplate.IsValid, putTemplate.DebugInformation); + Console.WriteLine($"Put Template: {putTemplate.IsValid}"); + + // Validate settings & mappings + createIndex = await client.Indices.CreateAsync("books-fiction-horror"); + Debug.Assert(createIndex.IsValid, createIndex.DebugInformation); + Console.WriteLine($"Create Index: {createIndex.IsValid}"); + + getIndex = await client.Indices.GetAsync("books-fiction-horror"); + Debug.Assert( + getIndex.Indices["books-fiction-horror"].Settings.NumberOfShards == 1, + "`books-fiction-horror` index should have 1 shard"); + Debug.Assert( + getIndex.Indices["books-fiction-horror"].Mappings.Properties["pages"].Type == "integer", + "`pages` property should have `integer` type"); + Console.WriteLine($"Number of shards: {getIndex.Indices["books-fiction-horror"].Settings.NumberOfShards}"); + Console.WriteLine($"`pages` property type: {getIndex.Indices["books-fiction-horror"].Mappings.Properties["pages"].Type}"); + + // Get index template + + var getTemplate = await client.Indices.GetComposableTemplateAsync("books"); + Debug.Assert( + getTemplate.IndexTemplates.First().IndexTemplate.IndexPatterns.First() == "books-*", + "First index pattern should be `books-*`"); + Console.WriteLine($"First index pattern: {getTemplate.IndexTemplates.First().IndexTemplate.IndexPatterns.First()}"); + + // Delete index template + + var deleteTemplate = await client.Indices.DeleteComposableTemplateAsync("books"); + Debug.Assert(deleteTemplate.IsValid, deleteTemplate.DebugInformation); + Console.WriteLine($"Delete Template: {deleteTemplate.IsValid}"); + + // Cleanup + + var deleteIndex = await client.Indices.DeleteAsync("books-*"); + Debug.Assert(deleteIndex.IsValid, deleteIndex.DebugInformation); + Console.WriteLine($"Delete Index: {deleteIndex.IsValid}"); + + deleteTemplate = await client.Indices.DeleteComposableTemplateAsync("books-fiction"); + Debug.Assert(deleteTemplate.IsValid, deleteTemplate.DebugInformation); + Console.WriteLine($"Delete Template: {deleteTemplate.IsValid}"); + + var deleteComponentTemplate = await client.Cluster.DeleteComponentTemplateAsync("books_mappings"); + Debug.Assert(deleteComponentTemplate.IsValid, deleteComponentTemplate.DebugInformation); + Console.WriteLine($"Delete Component Template: {deleteComponentTemplate.IsValid}"); + } + + private class Book + { + public string? Title { get; set; } + public string? Author { get; set; } + public DateTime? PublishedOn { get; set; } + public int? Pages { get; set; } + } +} diff --git a/samples/Samples/Program.cs b/samples/Samples/Program.cs new file mode 100644 index 0000000000..e4174840de --- /dev/null +++ b/samples/Samples/Program.cs @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +*/ + +using System.CommandLine; +using Samples.Utils; + +namespace Samples; + +public static class Program +{ + public static async Task Main(string[] args) + { + var rootCommand = new RootCommand("A collection of samples demonstrating how to use the OpenSearch .NET client"); + var clientDescriptor = rootCommand.AddOpenSearchClientOptions(); + + foreach (var sample in Sample.GetAllSamples()) rootCommand.AddCommand(sample.AsCommand(clientDescriptor)); + + await rootCommand.InvokeAsync(args); + } +} diff --git a/samples/Samples/RawJsonSample/Program.cs b/samples/Samples/RawJson/RawJsonSample.cs similarity index 86% rename from samples/Samples/RawJsonSample/Program.cs rename to samples/Samples/RawJson/RawJsonSample.cs index 2ad5796a72..62a75719ef 100644 --- a/samples/Samples/RawJsonSample/Program.cs +++ b/samples/Samples/RawJson/RawJsonSample.cs @@ -10,19 +10,16 @@ using OpenSearch.Net; using HttpMethod = OpenSearch.Net.HttpMethod; -public class Program -{ - public static async Task Main(string[] args) - { - var node = new Uri("http://localhost:9200"); - var config = new ConnectionSettings(node) - .ServerCertificateValidationCallback(CertificateValidations.AllowAll) - .BasicAuthentication("admin", "admin") - .DisableDirectStreaming(); +namespace Samples.RawJson; - var client = new OpenSearchClient(config); +public class RawJsonSample : Sample +{ + public RawJsonSample() : base("raw-json", "A sample demonstrating how to use the low-level client to perform raw JSON requests") { } + protected override async Task Run(IOpenSearchClient client) + { var info = await client.LowLevel.DoRequestAsync(HttpMethod.GET, "/", CancellationToken.None); + Debug.Assert(info.Success, info.DebugInformation); Console.WriteLine($"Welcome to {info.Body.version.distribution} {info.Body.version.number}!"); // Create an index diff --git a/samples/Samples/Sample.cs b/samples/Samples/Sample.cs new file mode 100644 index 0000000000..6126255c99 --- /dev/null +++ b/samples/Samples/Sample.cs @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +*/ + +using System.CommandLine; +using System.CommandLine.Binding; +using OpenSearch.Client; + +namespace Samples; + +public abstract class Sample +{ + public static IEnumerable GetAllSamples() => + typeof(Sample) + .Assembly + .GetTypes() + .Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(Sample))) + .Select(t => (Sample) Activator.CreateInstance(t)!); + + private readonly string _name; + private readonly string _description; + + protected Sample(string name, string description) + { + _name = name; + _description = description; + } + + public Command AsCommand(IValueDescriptor clientDescriptor) + { + var command = new Command(_name, _description); + + command.SetHandler(Run, clientDescriptor); + + return command; + } + + protected abstract Task Run(IOpenSearchClient client); +} diff --git a/samples/Samples/Samples.csproj b/samples/Samples/Samples.csproj index 684c46b599..6ac00aa311 100644 --- a/samples/Samples/Samples.csproj +++ b/samples/Samples/Samples.csproj @@ -12,4 +12,8 @@ + + + + \ No newline at end of file diff --git a/samples/Samples/Utils/OpenSearchClientOptions.cs b/samples/Samples/Utils/OpenSearchClientOptions.cs new file mode 100644 index 0000000000..ae42bdb387 --- /dev/null +++ b/samples/Samples/Utils/OpenSearchClientOptions.cs @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +*/ + +using System.CommandLine; +using System.CommandLine.Binding; +using OpenSearch.Client; +using OpenSearch.Net; + +namespace Samples.Utils; + +public static class OpenSearchClientOptions +{ + public static IValueDescriptor AddOpenSearchClientOptions(this Command command, bool global = true) + { + Option host = new("--host", () => new Uri("https://localhost:9200"), "The OpenSearch host to connect to"); + Option username = new("--username", () => "admin", "The username to use for authentication"); + Option password = new("--password", () => "admin", "The password to use for authentication"); + + Action