Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Added guide for using raw JSON REST requests with low-level client. #406

Merged
merged 3 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions OpenSearch.sln
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenSearch.Stack.ArtifactsA
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{22DF419F-9A90-4317-957D-E239EB3F95DF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{E89FE975-FA94-405F-B748-BF2EC8AFFEEE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples", "samples\Samples\Samples.csproj", "{0D084660-06BF-4F3A-A041-DAAB4837378F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -137,6 +141,7 @@ Global
{22DF419F-9A90-4317-957D-E239EB3F95DF} = {87ABA679-F3F4-48CE-82B3-1AAE5D0A5935}
{C80D225C-F072-4B24-9ACE-82EFD9362237} = {22DF419F-9A90-4317-957D-E239EB3F95DF}
{1F5A7B1A-2566-481F-91B5-A63D7F939973} = {22DF419F-9A90-4317-957D-E239EB3F95DF}
{0D084660-06BF-4F3A-A041-DAAB4837378F} = {E89FE975-FA94-405F-B748-BF2EC8AFFEEE}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5B393962-7586-49BA-BD99-3B1E35F48E94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -231,5 +236,9 @@ Global
{E7C0BDC2-28AD-4582-8FEA-0F6327A42C0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7C0BDC2-28AD-4582-8FEA-0F6327A42C0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7C0BDC2-28AD-4582-8FEA-0F6327A42C0E}.Release|Any CPU.Build.0 = Release|Any CPU
{0D084660-06BF-4F3A-A041-DAAB4837378F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D084660-06BF-4F3A-A041-DAAB4837378F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D084660-06BF-4F3A-A041-DAAB4837378F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D084660-06BF-4F3A-A041-DAAB4837378F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
7 changes: 7 additions & 0 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
- [OpenSearch.Net](#opensearchnet)
- [Getting Started](#getting-started-2)
- [Connecting](#connecting-2)
- [Advanced Features](#advanced-features)

# User Guide

This user guide specifies how to include and use the .NET client in your application.
Expand Down Expand Up @@ -303,3 +305,8 @@ var client = new OpenSearchLowLevelClient(config);
```

Note the main difference here is that we are instantiating an `OpenSearchLowLevelClient` rather than `OpenSearchClient`, and `ConnectionConfiguration` instead of `ConnectionSettings`.


## Advanced Features

- [Making Raw JSON Requests](guides/json.md)
131 changes: 131 additions & 0 deletions guides/json.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
- [Making Raw JSON REST Requests](#making-raw-json-rest-requests)
- [HTTP Methods](#http-methods)
- [GET](#get)
- [PUT](#put)
- [POST](#post)
- [DELETE](#delete)
- [Using Different Types Of PostData](#using-different-types-of-postdata)
- [PostData.String](#postdatastring)
- [PostData.Bytes](#postdatabytes)
- [PostData.Serializable](#postdataserializable)
- [PostData.MultiJson](#postdatamultijson)

# Making Raw JSON REST Requests
OpenSearch exposes a REST API that you can use to interact with OpenSearch. The OpenSearch .NET client provides a low-level API that allows you to send raw JSON requests to OpenSearch. This API is useful if you want to use a feature that is not yet supported by the OpenSearch .NET client, but it supported by the OpenSearch REST API.
Djcarrillo6 marked this conversation as resolved.
Show resolved Hide resolved

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.

## HTTP Methods

### GET
The following example returns the server version information via `GET /`.

```csharp
var info = await client.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.GET, "/", CancellationToken.None);
Console.WriteLine($"Welcome to {info.Body.version.distribution} {info.Body.version.number}!");
```

### PUT
The following example creates an index.

```csharp
var indexBody = new { settings = new { index = new { number_of_shards = 4 } } };

var createIndex = await client.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.PUT, "/movies", CancellationToken.None, PostData.Serializable(indexBody));
Debug.Assert(createIndex.Success && (bool)createIndex.Body.acknowledged, createIndex.DebugInformation);
```

### POST
The following example searches for a document.

```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.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.POST, $"/{indexName}/_search", CancellationToken.None, PostData.Serializable(query));
Debug.Assert(search.Success, search.DebugInformation);

foreach (var hit in search.Body.hits.hits) Console.WriteLine(hit["_source"]["title"]);
```

### DELETE
The following example deletes an index.

```csharp
var deleteDocument = await client.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.DELETE, $"/{indexName}/_doc/{id}", CancellationToken.None);
Debug.Assert(deleteDocument.Success, deleteDocument.DebugInformation);
```

## Using Different Types Of PostData
The OpenSearch .NET client provides a `PostData` class that is used to provide the request body for a request. The `PostData` class has several static methods that can be used to create a `PostData` object from different types of data.

### PostData.String
The following example shows how to use the `PostData.String` method to create a `PostData` object from a string.

```csharp
string indexBody = @"
{{
""settings"": {
""index"": {
""number_of_shards"": 4
}
}
}}";

await client.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.PUT, "/movies", CancellationToken.None, PostData.String(indexBody));
```

### PostData.Bytes
The following example shows how to use the `PostData.Bytes` method to create a `PostData` object from a byte array.

```csharp
byte[] indexBody = Encoding.UTF8.GetBytes(@"
{{
""settings"": {
""index"": {
""number_of_shards"": 4
}
}
}}");

await client.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.PUT, "/movies", CancellationToken.None, PostData.Bytes(indexBody));
```

### PostData.Serializable
The following example shows how to use the `PostData.Serializable` method to create a `PostData` object from a serializable object.

```csharp
var indexBody = new
{
settings = new
{
index = new
{
number_of_shards = 4
}
}
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a JSON/string version of this? Not sure what's best for this example, maybe both?


await client.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.PUT, "/movies", CancellationToken.None, PostData.Serializable(indexBody));
```

### PostData.MultiJson
The following example shows how to use the `PostData.MultiJson` method to create a `PostData` object from a collection of serializable objects.
The `PostData.MultiJson` method is useful when you want to send multiple documents in a bulk request.

```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.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.POST, "/_bulk", CancellationToken.None, PostData.MultiJson(bulkBody));
```
71 changes: 71 additions & 0 deletions samples/Samples/RawJsonSample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* 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;
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();

var client = new OpenSearchClient(config);

var info = await client.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.GET, "/", CancellationToken.None);
Console.WriteLine($"Welcome to {info.Body.version.distribution} {info.Body.version.number}!");

// Create an index

const string indexName = "movies";

var indexBody = new { settings = new { index = new { number_of_shards = 4 } } };

var createIndex = await client.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.PUT, $"/{indexName}", CancellationToken.None, PostData.Serializable(indexBody));
Debug.Assert(createIndex.Success && (bool)createIndex.Body.acknowledged, createIndex.DebugInformation);

// Add a document to the index
var document = new { title = "Moneyball", director = "Bennett Miller", year = 2011};

const string id = "1";

var addDocument = await client.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.PUT, $"/{indexName}/_doc/{id}", CancellationToken.None, PostData.Serializable(document));
Debug.Assert(addDocument.Success, addDocument.DebugInformation);

// Refresh the index
var refresh = await client.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.POST, $"/{indexName}/_refresh", CancellationToken.None);
Debug.Assert(refresh.Success, refresh.DebugInformation);

// Search for a document
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.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.POST, $"/{indexName}/_search", CancellationToken.None, PostData.Serializable(query));
Debug.Assert(search.Success, search.DebugInformation);

foreach (var hit in search.Body.hits.hits) Console.WriteLine(hit["_source"]["title"]);

// Delete the document
var deleteDocument = await client.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.DELETE, $"/{indexName}/_doc/{id}", CancellationToken.None);
Debug.Assert(deleteDocument.Success, deleteDocument.DebugInformation);

// Delete the index
var deleteIndex = await client.LowLevel.DoRequestAsync<DynamicResponse>(HttpMethod.DELETE, $"/{indexName}", CancellationToken.None);
Debug.Assert(deleteIndex.Success && (bool)deleteIndex.Body.acknowledged, deleteIndex.DebugInformation);
}
}
15 changes: 15 additions & 0 deletions samples/Samples/Samples.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Djcarrillo6 marked this conversation as resolved.
Show resolved Hide resolved
<IsPackable>False</IsPackable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="$(SolutionRoot)\src\OpenSearch.Client\OpenSearch.Client.csproj" />
</ItemGroup>

</Project>
Loading