### Start Ephemeral Cluster with Custom Configuration Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.OpenSearch.Ephemeral/README.md Configure and start a multi-node OpenSearch cluster with specific plugins and features. This example demonstrates creating a custom configuration, starting the cluster, and then interacting with it using an OpenSearch client. ```csharp var plugins = new OpenSearchPlugins(OpenSearchPlugin.RepositoryAzure, OpenSearchPlugin.IngestAttachment); var config = new EphemeralClusterConfiguration("1.0.0", ServerType.OpenSearch, ClusterFeatures.None, plugins, numberOfNodes: 2); using (var cluster = new EphemeralCluster(config)) { cluster.Start(); var nodes = cluster.NodesUris(); var connectionPool = new StaticConnectionPool(nodes); var settings = new ConnectionSettings(connectionPool).EnableDebugMode(); var client = new OpenSearchClient(settings); Console.Write(client.CatPlugins().DebugInformation); } ``` -------------------------------- ### GET Request Example Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/json.md Example of making a GET request to retrieve server version information. ```APIDOC ## GET / ### Description Retrieves the server version information. ### Method GET ### Endpoint / ### Response #### Success Response (200) - **Body** (DynamicResponse) - Contains server version details. ### Request Example ```csharp var info = await client.Http.GetAsync("/"); Console.WriteLine($"Welcome to {info.Body.version.distribution} {info.Body.version.number}!"); ``` ``` -------------------------------- ### Start OpenSearch Node with Custom Handlers Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.OpenSearch.Managed/README.md Starts an OpenSearch node, subscribes to its output lines using a HighlightWriter, and explicitly waits for the started state within a specified timeout. Throws an exception if the node does not start within the given time. ```csharp using (var node = new OpenSearchNode(version, openSearchHome)) { node.SubscribeLines(new HighlightWriter()); if (!node.WaitForStarted(TimeSpan.FromMinutes(2))) throw new Exception(); } ``` -------------------------------- ### Start OpenSearch Node Gracefully Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.OpenSearch.Managed/README.md Starts an OpenSearch node and ensures it shuts down gracefully when disposed. The default overload waits for a started confirmation and pretty-prints console output. ```csharp using (var node = new OpenSearchNode(version, openSearchHome)) { node.Start(); } ``` -------------------------------- ### Start OpenSearch Node with NodeConfiguration Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.OpenSearch.Managed/README.md Starts an OpenSearch node using a detailed NodeConfiguration object, allowing customization of port, output display, and node settings. Settings are passed as key-value pairs. ```csharp var clusterConfiguration = new ClusterConfiguration(version, ServerType.OpenSearch, openSearchHome); var nodeConfiguration = new NodeConfiguration(clusterConfiguration, 9200) { ShowOpenSearchOutputAfterStarted = false, Settings = { "node.attr.x", "y" } }; using (var node = new OpenSearchNode(nodeConfiguration)) { node.Start(); } ``` -------------------------------- ### Project Setup with OpenSearch.OpenSearch.Xunit Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.OpenSearch.Xunit/README.md Configure your .NET project to include the OpenSearch.OpenSearch.Xunit package and necessary testing tools. This setup supports both .NET Core and .NET Framework. ```xml netcoreapp3.0 ``` -------------------------------- ### Setup OpenSearch Client Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/index-template.md Establishes a connection to a local OpenSearch instance. Requires server certificate validation to be bypassed and basic authentication credentials. ```csharp using OpenSearch.Client; using OpenSearch.Net; var node = new Uri("https://localhost:9200"); var config = new ConnectionSettings(node) .ServerCertificateValidationCallback(CertificateValidations.AllowAll) .BasicAuthentication("admin", ); var client = new OpenSearchClient(config); ``` -------------------------------- ### PUT Request Example Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/json.md Example of making a PUT request to create an index with specific settings. ```APIDOC ## PUT /movies ### Description Creates a new index with specified settings. ### Method PUT ### Endpoint /movies ### Parameters #### Request Body - **settings** (object) - Required - Index settings, including number of shards. ### Request Example ```csharp var indexBody = new { settings = new { index = new { number_of_shards = 4 } } }; var createIndex = await client.Http.PutAsync("/movies", d => d.SerializableBody(indexBody)); Console.WriteLine($"Create Index: {createIndex.Success && (bool)createIndex.Body.acknowledged}"); ``` ``` -------------------------------- ### Start Ephemeral Cluster with Version Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.OpenSearch.Ephemeral/README.md Use this snippet to quickly start an OpenSearch cluster with a specified version. The cluster and its resources are managed ephemerally and cleaned up automatically. ```csharp using (var cluster = new EphemeralCluster("1.0.0")) { cluster.Start(); } ``` -------------------------------- ### HEAD Request Example Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/json.md Example of making a HEAD request to check if an index exists. ```APIDOC ## HEAD /movies ### Description Checks if the specified index exists. ### Method HEAD ### Endpoint /movies ### Response #### Success Response (200) - **HttpStatusCode** (int) - Indicates success if 200. ### Request Example ```csharp var indexExists = await client.Http.HeadAsync("/movies"); Console.WriteLine($"Index Exists: {indexExists.HttpStatusCode == 200}"); ``` ``` -------------------------------- ### Setup OpenSearch Index and Documents with .NET Client Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/search.md Initializes the OpenSearch client, defines a Movie class, creates an index with mappings, and indexes a list of Movie documents. Requires OpenSearch.Client and OpenSearch.Net NuGet packages. ```csharp using OpenSearch.Client; using OpenSearch.Net; var node = new Uri("https://localhost:9200"); var config = new ConnectionSettings(node) .ThrowExceptions() .ServerCertificateValidationCallback(CertificateValidations.AllowAll) .BasicAuthentication("admin", ); var client = new OpenSearchClient(config); class Movie { public string Title { get; set; } public string Director { get; set; } public int Year { get; set; } public override string ToString() { return $"{nameof(Title)}: {Title}, {nameof(Director)}: {Director}, {nameof(Year)}: {Year}"; } } await client.Indices.CreateAsync("movies", c => c .Settings(s => s .NumberOfShards(1) .NumberOfReplicas(0) ) .Map(m => m .Properties(p => p .Text(t => t .Name(o => o.Title)) .Text(t => t .Name(o => o.Director)) .Number(n => n .Name(o => o.Year) .Type(NumberType.Integer))))); var movies = new List { new Movie { Title = "The Godfather", Director = "Francis Ford Coppola", Year = 1972 }, new Movie { Title = "The Shawshank Redemption", Director = "Frank Darabont", Year = 1994 }, }; for (var i = 0; i < 10; ++i) movies.Add(new Movie { Title = "The Dark Knight " + i, Director = "Christopher Nolan", Year = 2008 + i }); // Index the movies var indexResponse = await client.BulkAsync(b => b .Index("movies") .IndexMany(movies) .Refresh(Refresh.WaitFor)); Console.WriteLine($"Indexed {indexResponse.Items.Count} movies"); // Indexed 12 movies ``` -------------------------------- ### POST Request Example Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/json.md Example of making a POST request to search for documents within an index. ```APIDOC ## POST /movies/_search ### Description Searches for documents within the 'movies' index based on a query. ### Method POST ### Endpoint /movies/_search ### Parameters #### Request Body - **query** (object) - Required - The search query definition. ### Request Example ```csharp const string q = "miller"; var query = new { size = 5, query = new { multi_match = new { query = q, fields = new[] { "title^2", "director" } } } }; var search = await client.Http.PostAsync("/movies/_search", d => d.SerializableBody(query)); foreach (var hit in search.Body.hits.hits) Console.WriteLine($"Search Hit: {hit["_source"]["title"]}"); ``` ``` -------------------------------- ### Check .NET SDK Version Source: https://github.com/opensearch-project/opensearch-net/blob/main/DEVELOPER_GUIDE.md Verify that the .NET 6.0 SDK is installed on your system. ```bash $ dotnet --list-sdks 6.0.xxx ``` -------------------------------- ### Add OpenSearch.Client NuGet Package Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Include the OpenSearch.Client NuGet package in your .csproj file to start using the client. ```xml ... ``` -------------------------------- ### Create an Integration Test Class Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.OpenSearch.Xunit/README.md Write integration tests by implementing IClusterFixture and injecting your custom cluster. This example shows how to obtain an OpenSearchClient and perform a basic test. ```csharp public class ExampleTest : IClusterFixture { public ExampleTest(MyTestCluster cluster) { // This registers a single client for the whole clusters lifetime to be reused and shared. // we do not expose Client on the passed cluster directly for two reasons // // 1) We do not want to prescribe how to new up the client // // 2) We do not want OpenSearch.Xunit to depend on OSC. OpenSearch.Xunit can start clusters // and OSC Major.x is only tested and supported against OpenSearch Major.x. // this.Client = cluster.GetOrAddClient(c => { var nodes = cluster.NodesUris(); var connectionPool = new StaticConnectionPool(nodes); var settings = new ConnectionSettings(connectionPool) .EnableDebugMode(); return new OpenSearchClient(settings); ); } private OpenSearchClient Client { get; } /// [I] marks an integration test (like [Fact] would for plain Xunit) [I] public void SomeTest() { var rootNodeInfo = this.Client.RootNodeInfo(); rootNodeInfo.Name.Should().NotBeNullOrEmpty(); } } ``` -------------------------------- ### Start OpenSearch Cluster Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.OpenSearch.Managed/README.md Starts a cluster of OpenSearch nodes based on the provided ClusterConfiguration. The cluster will manage the lifecycle of multiple OpenSearch nodes. ```csharp var clusterConfiguration = new ClusterConfiguration(version, ServerType.OpenSearch, openSearchHome, numberOfNodes: 2); using (var cluster = new OpenSearchCluster(clusterConfiguration)) { cluster.Start(); } ``` -------------------------------- ### Initialize OpenSearch Home Path Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.OpenSearch.Managed/README.md Defines the version and the local path for OpenSearch installation. This is a prerequisite for creating OpenSearchNode instances. ```csharp var version = "1.0.0"; var openSearchHome = Environment.ExpandEnvironmentVariables($"%LOCALAPPDATA%\OpenSearchManaged\{version}\opensearch-{version}"); ``` -------------------------------- ### Setup OpenSearch .NET Client and Define Movie Class Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/document-lifecycle.md Initializes the OpenSearch client with connection settings, including certificate validation and authentication. Defines a 'Movie' class for document structure. ```csharp var node = new Uri("https://localhost:9200"); var config = new ConnectionSettings(node) .ServerCertificateValidationCallback(CertificateValidations.AllowAll) .BasicAuthentication("admin", ) .DisableDirectStreaming(); var client = new OpenSearchClient(config); class Movie { public string Title { get; set; } public string Director { get; set; } public int? Year { get; set; } public override string ToString() { return $"{nameof(Title)}: {Title}, {nameof(Director)}: {Director}, {nameof(Year)}: {Year}"; } } ``` -------------------------------- ### Get Server Version Information Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/json.md Use the `Http.GetAsync` method to retrieve server version details by making a GET request to the root endpoint (/). ```csharp var info = await client.Http.GetAsync("/"); Console.WriteLine($"Welcome to {info.Body.version.distribution} {info.Body.version.number}!"); ``` -------------------------------- ### DELETE Request Example Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/json.md Example of making a DELETE request to remove an index. ```APIDOC ## DELETE /movies ### Description Deletes the specified index. ### Method DELETE ### Endpoint /movies ### Response #### Success Response (200) - **Body** (DynamicResponse) - Contains acknowledgment of deletion. ### Request Example ```csharp var deleteIndex = await client.Http.DeleteAsync("/movies"); Console.WriteLine($"Delete Index: {deleteIndex.Success && (bool)deleteIndex.Body.acknowledged}"); ``` ``` -------------------------------- ### Get Artifact for OpenSearch Version Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.Stack.ArtifactsApi/README.md Obtain an artifact for a specific OpenSearch version and product. First, create a Product object, then pass it to the version's Artifact method. ```csharp var product = Product.From("opensearch"); var artifact = version.Artifact(product); ``` -------------------------------- ### Get Artifact for Plugin Version Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.Stack.ArtifactsApi/README.md Retrieve an artifact for a specific OpenSearch version and a related product like a plugin. Use Product.From with the product name and plugin identifier. ```csharp var product = Product.From("opensearch-plugins", "analysis-icu"); var artifact = version.Artifact(product); ``` -------------------------------- ### Cleanup Resources Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/index-template.md Deletes all resources created in the guide, including indices and templates, to clean up the OpenSearch cluster. ```csharp var deleteIndex = await client.Indices.DeleteAsync("books-*"); Console.WriteLine($"Delete Index: {deleteIndex.IsValid}"); ``` ```csharp deleteTemplate = await client.Indices.DeleteComposableTemplateAsync("books-fiction"); Console.WriteLine($"Delete Template: {deleteTemplate.IsValid}"); ``` ```csharp var deleteComponentTemplate = await client.Cluster.DeleteComponentTemplateAsync("books_mappings"); Console.WriteLine($"Delete Component Template: {deleteComponentTemplate.IsValid}"); ``` -------------------------------- ### Delete Indices Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/bulk.md Clean up resources by deleting the indices created during the guide. This ensures no lingering data or overhead. ```cs await client.Indices.DeleteAsync(new[] { movies, books }); ``` -------------------------------- ### Paginate Search Results with Point in Time (PIT) Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/search.md Use the Point in Time (PIT) feature for consistent pagination, especially when the index might be updated during scrolling. Create a PIT with `CreatePitAsync`, then use its ID with the `PointInTime` parameter in subsequent `SearchAsync` calls. Use `SearchAfter` with the last hit's sort values to get the next page. ```csharp var pitResp = await client.CreatePitAsync("movies", p => p.KeepAlive("1m")); var page1 = await client.SearchAsync(s => s .Query(_ => query) .Sort(_ => sort) .Size(2) .PointInTime(p => p.Id(pitResp.PitId).KeepAlive("1m"))); var page2 = await client.SearchAsync(s => s .Query(_ => query) .Sort(_ => sort) .Size(2) .PointInTime(p => p.Id(pitResp.PitId).KeepAlive("1m")) .SearchAfter(page1.Hits.Last().Sorts)); var page3 = await client.SearchAsync(s => s .Query(_ => query) .Sort(_ => sort) .Size(2) .PointInTime(p => p.Id(pitResp.PitId).KeepAlive("1m")) .SearchAfter(page2.Hits.Last().Sorts)); foreach (var doc in page1.Documents.Concat(page2.Documents).Concat(page3.Documents)) { Console.WriteLine(doc.Title); } await client.DeletePitAsync(p => p.PitId(pitResp.PitId)); ``` -------------------------------- ### Get Document by ID Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/document-lifecycle.md Retrieves a document with a specific ID from the 'movies' index and prints its source content. ```csharp var getResponse = client.Get(1, g => g.Index("movies")); Console.WriteLine(getResponse.Source); // -> Title: The Godfather, Director: Francis Ford Coppola, Year: 1972 ``` -------------------------------- ### Pass Multi JSON for Bulk Request Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/json.md Provides an example of creating a bulk request body by passing a collection of objects that will be serialized to newline-delimited JSON. ```csharp var bulkBody = new object[] { new { index = new { _index = "movies", _id = "1" } }, new { title = "The Godfather", director = "Francis Ford Coppola", year = 1972 }, new { index = new { _index = "movies", _id = "2" } }, new { title = "The Godfather: Part II", director = "Francis Ford Coppola", year = 1974 } }; await client.Http.PostAsync("/_bulk", d => d.MultiJsonBody(indexBody)); ``` -------------------------------- ### Get Document with Source Includes Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/document-lifecycle.md Retrieves a document by ID, specifying that only the 'Title' field should be included in the source response. ```csharp var getResponse2 = client.Get(1, g => g .Index("movies") .SourceIncludes(m => m.Title)); Console.WriteLine(getResponse2.Source); // -> Title: The Godfather, Director: , Year: ``` -------------------------------- ### Get Multiple Documents Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/document-lifecycle.md Retrieve multiple documents efficiently using the `MultiGet` API action or the `GetMany` helper method. This allows fetching documents by their IDs from a specified index. ```APIDOC ## Get Multiple Documents To get multiple documents, use the `MultiGet` API action or the `GetMany` helper. The following code gets the documents with IDs `1` and `2` from the `movies` index: ```csharp var mgIds = new long[] { 1, 2 }; var multiGetResponse = client.MultiGet(mg => mg .GetMany(mgIds, (g, id) => g.Index("movies"))); Console.WriteLine(string.Join('\n', multiGetResponse.SourceMany(mgIds))); // OR: var multiGetHits = client.GetMany(mgIds, "movies"); Console.WriteLine(string.Join('\n', multiGetHits.Select(h => h.Source))); // -> Title: The Godfather, Director: Francis Ford Coppola, Year: 1972 // -> Title: The Godfather: Part IV, Director: Francis Ford Coppola, Year: 1974 ``` ``` -------------------------------- ### Get Multiple Documents using MultiGet API Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/document-lifecycle.md Use the `MultiGet` API action or `GetMany` helper to retrieve multiple documents by their IDs from a specified index. Ensure the `Movie` type is defined for deserialization. ```csharp var mgIds = new long[] { 1, 2 }; var multiGetResponse = client.MultiGet(mg => mg .GetMany(mgIds, (g, id) => g.Index("movies"))); Console.WriteLine(string.Join('\n', multiGetResponse.SourceMany(mgIds))); ``` ```csharp // OR: var multiGetHits = client.GetMany(mgIds, "movies"); Console.WriteLine(string.Join('\n', multiGetHits.Select(h => h.Source))); ``` -------------------------------- ### Initialize OpenSearch Client Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/bulk.md Create an instance of the OpenSearch client to interact with your OpenSearch cluster. ```cs var nodeAddress = new Uri("http://myserver:9200"); var client = new OpenSearchClient(nodeAddress); ``` -------------------------------- ### Create an Index with Settings Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/json.md Demonstrates creating an index with specific settings using `Http.PutAsync` and providing a serializable object for the request body. ```csharp var indexBody = new { settings = new { index = new { number_of_shards = 4 } } }; var createIndex = await client.Http.PutAsync("/movies", d => d.SerializableBody(indexBody)); Console.WriteLine($"Create Index: {createIndex.Success && (bool)createIndex.Body.acknowledged}"); ``` -------------------------------- ### Create Multiple Index Templates Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/index-template.md Demonstrates creating two composable index templates: 'books' with default settings and 'books-fiction' with higher priority and different settings. This allows for more specific template application based on index name patterns. ```csharp 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 ``` -------------------------------- ### Create Index Template with Mappings Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/index-template.md Creates a composable index template named 'books'. It specifies index patterns ('books-*'), priority, and defines settings (shards, replicas) and mappings for the 'Book' class. ```csharp 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 ``` -------------------------------- ### Get Composable Index Template Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/index-template.md Retrieves a specific composable index template by its name, allowing inspection of its configuration. ```csharp var getTemplate = await client.Indices.GetComposableTemplateAsync("books"); Console.WriteLine($"First index pattern: {getTemplate.IndexTemplates.First().IndexTemplate.IndexPatterns.First()}"); ``` -------------------------------- ### Run Integration Tests Source: https://github.com/opensearch-project/opensearch-net/blob/main/DEVELOPER_GUIDE.md Execute the build script to run the full suite of integration tests. This will automatically download and set up a local OpenSearch cluster. ```bash .\build.bat ``` ```bash build.sh ``` -------------------------------- ### Get Document with Source Excludes Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/document-lifecycle.md Retrieves a document by ID, specifying that the 'Title' field should be excluded from the source response. ```csharp var getResponse3 = client.Get(1, g => g .Index("movies") .SourceExcludes(m => m.Title)); Console.WriteLine(getResponse3.Source); // -> Title: , Director: Francis Ford Coppola, Year: 1972 ``` -------------------------------- ### Get a Document by ID Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Retrieve a document from an OpenSearch index using its ID and the index name. The response is mapped to the original document type. ```csharp var getResponse = client.Get(indexingResponse.Id, idx => idx.Index("mytweetindex")); // returns an IGetResponse mapped 1-to-1 with the OpenSearch JSON response var tweet = getResponse.Source; // the original document ``` -------------------------------- ### Paginate Search Results with SearchAfter and Sorting using .NET Client Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/search.md Demonstrates multi-page retrieval using 'SearchAfter' for deep pagination, fetching subsequent pages based on the sort values of the last document from the previous page. Requires a query, sort descriptor, and the sort values from the prior result. ```csharp var page1 = await client.SearchAsync(s => s .Index("movies") .Query(_ => query) .Sort(_ => sort) .Size(2)); var page2 = await client.SearchAsync(s => s .Index("movies") .Query(_ => query) .Sort(_ => sort) .Size(2) .SearchAfter(page1.Hits.Last().Sorts)); var page3 = await client.SearchAsync(s => s .Index("movies") .Query(_ => query) .Sort(_ => sort) .Size(2) .SearchAfter(page2.Hits.Last().Sorts)); Console.WriteLine(string.Join('\n', page3.Documents)); ``` -------------------------------- ### Connect to OpenSearch Cluster Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Instantiate an OpenSearchLowLevelClient with a ConnectionConfiguration pointing to your OpenSearch node. This is similar to OpenSearch.Client but uses specific low-level constructs. ```csharp var node = new Uri("http://myserver:9200"); var config = new ConnectionConfiguration(node); var client = new OpenSearchLowLevelClient(config); ``` -------------------------------- ### Add OpenSearch.Net to Project Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Include the OpenSearch.Net package in your .csproj file to add the library to your project. ```xml ... ``` -------------------------------- ### Add OpenSearch.Net.Auth.AwsSigV4 NuGet Package Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Include the OpenSearch.Net.Auth.AwsSigV4 package in your .csproj file to enable AWS SigV4 authentication. ```xml ... ``` -------------------------------- ### Create Index and Verify Mappings Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/index-template.md Creates an index named 'books-nonfiction' which automatically applies the 'books' index template. It then retrieves and verifies the 'pages' property type from the index mappings. ```csharp 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 ``` -------------------------------- ### Access Static Product Definitions Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.Stack.ArtifactsApi/README.md Utilize static properties for common product definitions to simplify artifact retrieval. This avoids needing to manually construct product monikers. ```csharp Product.OpenSearch; ``` ```csharp Product.OpenSearchDashboards; ``` ```csharp Product.OpenSearchPlugin(OpenSearchPlugin.AnalysisIcu); ``` -------------------------------- ### Generate Client Code Source: https://github.com/opensearch-project/opensearch-net/blob/main/DEVELOPER_GUIDE.md Use the build script to generate client code from the OpenAPI specification. Specify the branch, whether to include high-level clients, and if the specification should be downloaded. ```bash ./build.sh codegen --branch main --include-high-level --download ``` ```bash ./build.bat codegen --branch main --include-high-level --download ``` -------------------------------- ### Attempt to Create Existing Document (Fails) Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/document-lifecycle.md Demonstrates that attempting to `Create` a document with an ID that already exists will result in an invalid response. ```csharp var createResponse2 = client.Create(new Movie { Title = "The Godfather: Part II", Director = "Francis Ford Coppola", Year = 1974 }, i => i.Index("movies").Id(2)); Debug.Assert(createResponse2.IsValid, createResponse2.DebugInformation); // Will fail because the document with ID 2 already exists ``` -------------------------------- ### Create and Push Git Tag for Release Source: https://github.com/opensearch-project/opensearch-net/blob/main/RELEASING.md Use this command to create a tag for a specific commit and push it to the upstream repository, initiating the release process. ```bash git fetch origin git tag git push origin ``` -------------------------------- ### Search Documents with Object Initializer Syntax Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Perform a search query using object initializers for a more declarative approach. Define the search request object with properties like From, Size, and Query. ```csharp var request = new SearchRequest { From = 0, Size = 10, Query = new TermQuery { Field = "user", Value = "kimchy" } || new MatchQuery { Field = "description", Query = "OSC" } }; var searchResponse = client.Search(request); ``` -------------------------------- ### Paginate Search Results with From/Size and Sorting using .NET Client Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/search.md Retrieves a specific subset of search results ('dark knight' matches) by skipping a number of documents ('From') and limiting the number returned ('Size'), ordered by 'Year' ascending. Requires a query and sort descriptor. ```csharp var query = Query.Match(m => m .Field(f => f.Title) .Query("dark knight")); var sort = new SortDescriptor() .Ascending(f => f.Year); var searchResponse = await client.SearchAsync(s => s .Index("movies") .Query(_ => query) .Sort(_ => sort) .From(5) .Size(2)); Console.WriteLine(string.Join('\n', searchResponse.Documents)); ``` -------------------------------- ### Create Index with Composable Templates Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/index-template.md Creates an index that will automatically apply settings and mappings from matching composable templates based on its name and the defined index patterns and priorities. ```csharp createIndex = await client.Indices.CreateAsync("books-fiction-horror"); Console.WriteLine($"Create Index: {createIndex.IsValid}"); ``` -------------------------------- ### Verify Index Settings and Mappings Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/index-template.md Retrieves an index and verifies its settings and mappings, confirming that the composable templates were applied correctly. ```csharp 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}"); ``` -------------------------------- ### Connect OpenSearch.Net with AWS SigV4 Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Configure the low-level OpenSearch.Net client to use AWS SigV4 authentication by providing an AwsSigV4HttpConnection instance. ```csharp var endpoint = new Uri("https://example-aaabbbcccddd111222333.us-east-1.es.amazonaws.com"); var connection = new AwsSigV4HttpConnection(); var config = new ConnectionConfiguration(endpoint, connection); var client = new OpenSearchLowLevelClient(config); ``` -------------------------------- ### Create Index with Higher Priority Template Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/index-template.md Creates an index named 'books-fiction-romance'. OpenSearch applies the 'books-fiction' template due to its higher priority and matching index pattern. The number of shards is then verified. ```csharp 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 ``` -------------------------------- ### Request Body: String Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/json.md Demonstrates how to send a request body as a plain string. ```APIDOC ## PUT /movies (String Body) ### Description Creates an index using a string as the request body. ### Method PUT ### Endpoint /movies ### Parameters #### Request Body - **body** (string) - Required - JSON string representing index settings. ### Request Example ```csharp string indexBody = @" {{ "settings": {{ "index": {{ "number_of_shards": 4 }} }} }}"; await client.Http.PutAsync("/movies", d => d.Body(indexBody)); ``` ``` -------------------------------- ### Create OpenSearch Version Object Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.Stack.ArtifactsApi/README.md Create an OpenSearchVersion object from a version string. The version string can represent snapshot builds or released versions. ```csharp var version = OpenSearchVersion.From(versionString); ``` -------------------------------- ### Perform a Match All Search with .NET Client Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/search.md Searches for all documents within the 'movies' index. This is a basic query to retrieve all content. ```csharp var searchResponse = await client.SearchAsync(s => s .Index("movies") .Query(q => q.MatchAll())); Console.WriteLine(string.Join('\n', searchResponse.Documents)); ``` -------------------------------- ### Connect OpenSearch.Client with AWS SigV4 Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Configure OpenSearch.Client to use AWS SigV4 authentication by providing an AwsSigV4HttpConnection instance. ```csharp var endpoint = new Uri("https://example-aaabbbcccddd111222333.us-east-1.es.amazonaws.com"); var connection = new AwsSigV4HttpConnection(); var config = new ConnectionSettings(endpoint, connection); var client = new OpenSearchClient(config); ``` -------------------------------- ### Create OpenSearch Index with Mapped Document Type Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/document-lifecycle.md Creates an index named 'movies' and maps the 'Movie' document type, defining properties for Title, Director, and Year. ```csharp var createIndexResponse = client.Indices.Create("movies", c => c .Map(m => m .Properties(p => p .Text(t => t .Name(o => o.Title)) .Text(t => t .Name(o => o.Director)) .Number(n => n .Name(o => o.Year) .Type(NumberType.Integer)))))); Debug.Assert(createIndexResponse.IsValid, createIndexResponse.DebugInformation); ``` -------------------------------- ### Create Multiple Documents Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/bulk.md Create multiple documents efficiently using the `CreateMany` method for a specified index, or individually with `Create` for different indices within the same bulk request. IDs can be omitted to let OpenSearch generate them. ```cs var response = await client.BulkAsync(b => b .Index(movies) .CreateMany(new[] { new { Title = "Beauty and the Beast 2", Year = 2030 }, new { Title = "Beauty and the Beast 3", Year = 2031 }, new { Title = "Beauty and the Beast 4", Year = 2049 } }) .Create(i => i .Index(books) .Document(new { Title = "The Lion King 2", Year = 1998 } ))); ``` -------------------------------- ### Use OpenSearch.Net Low-Level Client Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Access the low-level OpenSearch.Net client from OpenSearch.Client to perform direct API calls. Useful when specific functionality is not yet exposed by the higher-level client. ```csharp IOpenSearchLowLevelClient lowLevelClient = client.LowLevel; // Generic parameter of Search<> is the type of .Body on response var response = lowLevelClient.Search>("mytweetindex", PostData.Serializable(new { from = 0, size = 10, fields = new [] {"id", "name"}, query = new { term = new { name = new { value= "OSC" } } } })); ``` -------------------------------- ### Connect to a Single OpenSearch Node Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Connect to a single OpenSearch cluster node using its URI. This is the simplest connection method. ```csharp var node = new Uri("http://myserver:9200"); var config = new ConnectionSettings(node); var client = new OpenSearchClient(config); ``` -------------------------------- ### Connect to Multiple OpenSearch Nodes with a Connection Pool Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Connect to multiple OpenSearch cluster nodes using a StaticConnectionPool for load balancing and failover. ```csharp var nodes = new Uri[] { new Uri("http://myserver1:9200"), new Uri("http://myserver2:9200"), new Uri("http://myserver3:9200") }; var pool = new StaticConnectionPool(nodes); var settings = new ConnectionSettings(pool); var client = new OpenSearchClient(settings); ``` -------------------------------- ### Register OpenSearch Test Framework Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.OpenSearch.Xunit/README.md Add this assembly attribute to inform Xunit to use the OpenSearch.OpenSearch.Xunit test framework for orchestrating and discovering tests. ```csharp [assembly: Xunit.TestFrameworkAttribute("OpenSearch.OpenSearch.Xunit.Sdk.OpenSearchTestFramework", "OpenSearch.OpenSearch.Xunit")] ``` -------------------------------- ### Define a Custom Test Cluster Source: https://github.com/opensearch-project/opensearch-net/blob/main/abstractions/src/OpenSearch.OpenSearch.Xunit/README.md Create a class that inherits from XunitClusterBase to define your integration test cluster. Configure the cluster version and any other necessary options. ```csharp /// Declare our cluster that we want to inject into our test classes public class MyTestCluster : XunitClusterBase { /// /// We pass our configuration instance to the base class. /// We only configure it to run version 1.0.0 here but lots of additional options are available. /// public MyTestCluster() : base(new XunitClusterConfiguration("1.0.0")) { } } ``` -------------------------------- ### Create Indices if Not Exists Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/bulk.md Ensure that the target indices for bulk operations exist before proceeding. This code checks for the existence of 'movies' and 'books' indices and creates them if they are missing. ```cs var movies = "movies"; var books = "books"; if (!(await client.Indices.ExistsAsync(movies)).Exists) { await client.Indices.CreateAsync(movies); } if (!(await client.Indices.ExistsAsync(books)).Exists) { await client.Indices.CreateAsync(books); } ``` -------------------------------- ### Configure AWS SigV4 with Explicit Credentials (Assume Role) Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Explicitly set AWS credentials, such as assuming a role, while allowing the region to be sourced from the environment. ```shell export AWS_REGION="ap-southeast-2" ``` ```csharp var endpoint = new Uri("https://example-aaabbbcccddd111222333.ap-southeast-2.es.amazonaws.com"); var credentials = new AssumeRoleAWSCredentials( FallbackCredentialsFactory.GetCredentials(), "arn:aws:iam::123456789012:role/my-open-search-ingest-role", "my-ingest-application"); var connection = new AwsSigV4HttpConnection(credentials); var config = new ConnectionSettings(endpoint, connection); var client = new OpenSearchClient(config); ``` -------------------------------- ### Create Component and Composable Templates Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/index-template.md Defines a component template for mappings and then creates two composable templates that utilize this component template with different index patterns and settings. Higher priority templates override settings from lower priority ones. ```csharp 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}"); ``` ```csharp 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}"); ``` ```csharp 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}"); ``` -------------------------------- ### Mix and Match Bulk Operations Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/bulk.md Perform a combination of create, update, and delete operations within a single bulk request for maximum efficiency. ```cs var response = await client.BulkAsync(b => b .Index(movies) .CreateMany(new[] { new { Title = "Beauty and the Beast 5", Year = 2050 }, new { Title = "Beauty and the Beast 6", Year = 2051 } }) .Update(i => i .Id(3) .Doc(new { Year = 2052 }) ) .Delete(i => i.Id(4))); ``` -------------------------------- ### Request Body: Bytes Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/json.md Demonstrates how to send a request body as a byte array. ```APIDOC ## PUT /movies (Bytes Body) ### Description Creates an index using a byte array as the request body. ### Method PUT ### Endpoint /movies ### Parameters #### Request Body - **body** (byte[]) - Required - Byte array representing the JSON request body. ### Request Example ```csharp byte[] indexBody = Encoding.UTF8.GetBytes(@" {{ "settings": {{ "index": {{ "number_of_shards": 4 }} }} }}"); await client.Http.PutAsync("/movies", d => d.Body(indexBody)); ``` ``` -------------------------------- ### Configure AWS SigV4 with Explicit Credentials and Region Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Explicitly set both AWS credentials (e.g., assuming a role) and the region for the connection. ```csharp var endpoint = new Uri("https://example-aaabbbcccddd111222333.ap-southeast-2.es.amazonaws.com"); var credentials = new AssumeRoleAWSCredentials( FallbackCredentialsFactory.GetCredentials(), "arn:aws:iam::123456789012:role/my-open-search-ingest-role", "my-ingest-application"); var connection = new AwsSigV4HttpConnection(credentials, RegionEndpoint.APSoutheast2); var config = new ConnectionSettings(endpoint, connection); var client = new OpenSearchClient(config); ``` -------------------------------- ### Create Document with Specified ID Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/document-lifecycle.md Uses the `Create` API action to add a new document with a specific ID. This operation is not idempotent and will fail if the ID already exists. ```csharp var createResponse = client.Create(new Movie { Title = "The Godfather", Director = "Francis Ford Coppola", Year = 1972 }, i => i.Index("movies").Id(1)); Debug.Assert(createResponse.IsValid, createResponse.DebugInformation); ``` -------------------------------- ### Delete an Index Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/json.md Shows how to delete an index using the `Http.DeleteAsync` method. ```csharp var deleteIndex = await client.Http.DeleteAsync("/movies"); Console.WriteLine($"Delete Index: {deleteIndex.Success && (bool)deleteIndex.Body.acknowledged}"); ``` -------------------------------- ### Index Document to Overwrite Existing Source: https://github.com/opensearch-project/opensearch-net/blob/main/guides/document-lifecycle.md Shows how the `Index` action overwrites an existing document when the same ID is used. This is an idempotent operation. ```csharp var indexResponse2 = client.Index(new Movie { Title = "The Godfather: Part III", Director = "Francis Ford Coppola", Year = 1974 }, i => i.Index("movies").Id(2)); Debug.Assert(indexResponse2.IsValid, indexResponse2.DebugInformation); // Succeeds and overwrites the existing document var indexResponse3 = client.Index(new Movie { Title = "The Godfather: Part IV", Director = "Francis Ford Coppola", Year = 1974 }, i => i.Index("movies").Id(2)); Debug.Assert(indexResponse3.IsValid, indexResponse3.DebugInformation); ``` -------------------------------- ### Search for Empty Term Values (Verbatim) Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Search for documents containing an exact, non-null, empty string value in a field. Use IsVerbatim = true or .Verbatim() to prevent empty values from being stripped. ```csharp var request = new SearchRequest { From = 0, Size = 10, Query = new TermQuery { Field = "user", Value = "", IsVerbatim = true }, }; var searchResponse = client.Search(request); ``` ```csharp var result = await OpenSearchClient.SearchAsync(s => s .Index(index) .From(0) .Size(10) .Query(q => q .Bool(b => b .Must(m => m.Term(t => t.Verbatim().Field(f => f.User).Value(string.Empty))) ) ) ); ``` -------------------------------- ### Search Documents with Fluent API Source: https://github.com/opensearch-project/opensearch-net/blob/main/USER_GUIDE.md Perform a search query using the fluent interface, specifying index, pagination, and query clauses. Supports complex queries like Term and Match. ```csharp var searchResponse = client.Search(s => s .Index("mytweetindex") //or specify index via settings.DefaultIndex("mytweetindex"); .From(0) .Size(10) .Query(q => q .Term(t => t.User, "kimchy") || q .Match(mq => mq.Field(f => f.User).Query("osc")) ) ); ```