Getting started with the high-level .NET client (OpenSearch.Client)

OpenSearch.Client is a high-level .NET client. It provides strongly typed requests and responses as well as Query DSL. It frees you from constructing raw JSON requests and parsing raw JSON responses by providing models that parse and serialize/deserialize requests and responses automatically. OpenSearch.Client also exposes the OpenSearch.Net low-level client if you need it.

This getting started guide illustrates how to connect to OpenSearch, index documents, and run queries.

Installing OpenSearch.Client

To install OpenSearch.Client, download the OpenSearch.Client NuGet package and add it to your project in an IDE of your choice. In Microsoft Visual Studio, follow the steps below:

  • In the Solution Explorer panel, right-click on your solution or project and select Manage NuGet Packages for Solution.
  • Search for the OpenSearch.Client NuGet package, and select Install.

Alternatively, you can add OpenSearch.Client to your .csproj file:

  1. <Project>
  2. ...
  3. <ItemGroup>
  4. <PackageReference Include="OpenSearch.Client" Version="1.0.0" />
  5. </ItemGroup>
  6. </Project>

Example

The following example illustrates connecting to OpenSearch, indexing documents, and sending queries on the data. It uses the Student class to represent one student, which is equivalent to one document in the index.

  1. public class Student
  2. {
  3. public int Id { get; init; }
  4. public string FirstName { get; init; }
  5. public string LastName { get; init; }
  6. public int GradYear { get; init; }
  7. public double Gpa { get; init; }
  8. }

By default, OpenSearch.Client uses camel case to convert property names to field names.

Connecting to OpenSearch

Use the default constructor when creating an OpenSearchClient object to connect to the default OpenSearch host (http://localhost:9200).

  1. var client = new OpenSearchClient();

To connect to your OpenSearch cluster through a single node with a known address, specify this address when creating an instance of OpenSearch.Client:

  1. var nodeAddress = new Uri("http://myserver:9200");
  2. var client = new OpenSearchClient(nodeAddress);

You can also connect to OpenSearch through multiple nodes. Connecting to your OpenSearch cluster with a node pool provides advantages like load balancing and cluster failover support. To connect to your OpenSearch cluster using multiple nodes, specify their addresses and create a ConnectionSettings object for the OpenSearch.Client instance:

  1. var nodes = new Uri[]
  2. {
  3. new Uri("http://myserver1:9200"),
  4. new Uri("http://myserver2:9200"),
  5. new Uri("http://myserver3:9200")
  6. };
  7. var pool = new StaticConnectionPool(nodes);
  8. var settings = new ConnectionSettings(pool);
  9. var client = new OpenSearchClient(settings);

Using ConnectionSettings

ConnectionConfiguration is used to pass configuration options to the low-level OpenSearch.Net client. ConnectionSettings inherits from ConnectionConfiguration and provides additional configuration options. To set the address of the node and the default index name for requests that don’t specify the index name, create a ConnectionSettings object:

  1. var node = new Uri("http://myserver:9200");
  2. var config = new ConnectionSettings(node).DefaultIndex("students");
  3. var client = new OpenSearchClient(config);

Indexing one document

Create one instance of Student:

  1. var student = new Student { Id = 100, FirstName = "Paulo", LastName = "Santos", Gpa = 3.93, GradYear = 2021 };

To index one document, you can use either fluent lambda syntax or object initializer syntax.

Index this Student into the students index using fluent lambda syntax:

  1. var response = client.Index(student, i => i.Index("students"));

Index this Student into the students index using object initializer syntax:

  1. var response = client.Index(new IndexRequest<Student>(student, "students"));

Indexing many documents

You can index many documents from a collection at the same time by using the OpenSearch.Client’s IndexMany method:

  1. var studentArray = new Student[]
  2. {
  3. new() {Id = 200, FirstName = "Shirley", LastName = "Rodriguez", Gpa = 3.91, GradYear = 2019},
  4. new() {Id = 300, FirstName = "Nikki", LastName = "Wolf", Gpa = 3.87, GradYear = 2020}
  5. };
  6. var manyResponse = client.IndexMany(studentArray, "students");

Searching for a document

To search for a student indexed above, you want to construct a query that is analogous to the following Query DSL query:

  1. GET students/_search
  2. {
  3. "query" : {
  4. "match": {
  5. "lastName": "Santos"
  6. }
  7. }
  8. }

The query above is a shorthand version of the following explicit query:

  1. GET students/_search
  2. {
  3. "query" : {
  4. "match": {
  5. "lastName": {
  6. "query": "Santos"
  7. }
  8. }
  9. }
  10. }

In OpenSearch.Client, this query looks like this:

  1. var searchResponse = client.Search<Student>(s => s
  2. .Index("students")
  3. .Query(q => q
  4. .Match(m => m
  5. .Field(fld => fld.LastName)
  6. .Query("Santos"))));

You can print out the results by accessing the documents in the response:

  1. if (searchResponse.IsValid)
  2. {
  3. foreach (var s in searchResponse.Documents)
  4. {
  5. Console.WriteLine($"{s.Id} {s.LastName} {s.FirstName} {s.Gpa} {s.GradYear}");
  6. }
  7. }

The response contains one document, which corresponds to the correct student:

100 Santos Paulo 3.93 2021

Using OpenSearch.Client methods asynchronously

For applications that require asynchronous code, all method calls in OpenSearch.Client have asynchronous counterparts:

  1. // synchronous method
  2. var response = client.Index(student, i => i.Index("students"));
  3. // asynchronous method
  4. var response = await client.IndexAsync(student, i => i.Index("students"));

Falling back on the low-level OpenSearch.Net client

OpenSearch.Client exposes the low-level the OpenSearch.Net client you can use if anything is missing:

  1. var lowLevelClient = client.LowLevel;
  2. var searchResponseLow = lowLevelClient.Search<SearchResponse<Student>>("students",
  3. PostData.Serializable(
  4. new
  5. {
  6. query = new
  7. {
  8. match = new
  9. {
  10. lastName = new
  11. {
  12. query = "Santos"
  13. }
  14. }
  15. }
  16. }));
  17. if (searchResponseLow.IsValid)
  18. {
  19. foreach (var s in searchResponseLow.Documents)
  20. {
  21. Console.WriteLine($"{s.Id} {s.LastName} {s.FirstName} {s.Gpa} {s.GradYear}");
  22. }
  23. }

Sample program

The following is a complete sample program that illustrates all of the concepts described above. It uses the Student class defined above.

  1. using OpenSearch.Client;
  2. using OpenSearch.Net;
  3. namespace NetClientProgram;
  4. internal class Program
  5. {
  6. private static IOpenSearchClient osClient = new OpenSearchClient();
  7. public static void Main(string[] args)
  8. {
  9. Console.WriteLine("Indexing one student......");
  10. var student = new Student { Id = 100,
  11. FirstName = "Paulo",
  12. LastName = "Santos",
  13. Gpa = 3.93,
  14. GradYear = 2021 };
  15. var response = osClient.Index(student, i => i.Index("students"));
  16. Console.WriteLine(response.IsValid ? "Response received" : "Error");
  17. Console.WriteLine("Searching for one student......");
  18. SearchForOneStudent();
  19. Console.WriteLine("Searching using low-level client......");
  20. SearchLowLevel();
  21. Console.WriteLine("Indexing an array of Student objects......");
  22. var studentArray = new Student[]
  23. {
  24. new() { Id = 200,
  25. FirstName = "Shirley",
  26. LastName = "Rodriguez",
  27. Gpa = 3.91,
  28. GradYear = 2019},
  29. new() { Id = 300,
  30. FirstName = "Nikki",
  31. LastName = "Wolf",
  32. Gpa = 3.87,
  33. GradYear = 2020}
  34. };
  35. var manyResponse = osClient.IndexMany(studentArray, "students");
  36. Console.WriteLine(manyResponse.IsValid ? "Response received" : "Error");
  37. }
  38. private static void SearchForOneStudent()
  39. {
  40. var searchResponse = osClient.Search<Student>(s => s
  41. .Index("students")
  42. .Query(q => q
  43. .Match(m => m
  44. .Field(fld => fld.LastName)
  45. .Query("Santos"))));
  46. PrintResponse(searchResponse);
  47. }
  48. private static void SearchLowLevel()
  49. {
  50. // Search for the student using the low-level client
  51. var lowLevelClient = osClient.LowLevel;
  52. var searchResponseLow = lowLevelClient.Search<SearchResponse<Student>>
  53. ("students",
  54. PostData.Serializable(
  55. new
  56. {
  57. query = new
  58. {
  59. match = new
  60. {
  61. lastName = new
  62. {
  63. query = "Santos"
  64. }
  65. }
  66. }
  67. }));
  68. PrintResponse(searchResponseLow);
  69. }
  70. private static void PrintResponse(ISearchResponse<Student> response)
  71. {
  72. if (response.IsValid)
  73. {
  74. foreach (var s in response.Documents)
  75. {
  76. Console.WriteLine($"{s.Id} {s.LastName} " +
  77. $"{s.FirstName} {s.Gpa} {s.GradYear}");
  78. }
  79. }
  80. else
  81. {
  82. Console.WriteLine("Student not found.");
  83. }
  84. }
  85. }