Link Search Menu Expand Document Documentation Menu

低级别 .NET 客户端 (OpenSearch.Net)

OpenSearch.Net 是一个低级别 .NET 客户端,提供了与 OpenSearch 通信的基础层。它不依赖于其他组件,可以处理轮询负载均衡、传输以及基本的请求/响应循环。OpenSearch.Net 包含所有 OpenSearch API 端点作为方法。使用 OpenSearch.Net 时,您需要自行构建查询。

本入门指南介绍了如何连接到 OpenSearch、索引文档和运行查询。有关客户端源代码,请参阅 opensearch-net 仓库

稳定版本

本文档反映了 GitHub 仓库中可用的最新更新,可能包含当前稳定版本中尚未提供的更改。NuGet 中当前的稳定版本是 1.2.0

示例

以下示例说明了如何连接到 OpenSearch、索引文档以及对数据发送查询。它使用 Student 类来表示一个学生,相当于索引中的一个文档。

public class Student
{
    public int Id { get; init; }
    public string FirstName { get; init; }
    public string LastName { get; init; }
    public int GradYear { get; init; }
    public double Gpa { get; init; }
}

安装 Opensearch.Net 客户端

要安装 Opensearch.Net,请下载 Opensearch.Net NuGet 包,并将其添加到您选择的 IDE 中的项目中。在 Microsoft Visual Studio 中,请按照以下步骤操作

  • 解决方案资源管理器面板中,右键单击您的解决方案或项目,然后选择管理解决方案的 NuGet 包
  • 搜索 OpenSearch.Net NuGet 包,然后选择安装

或者,您可以将 OpenSearch.Net 添加到您的 .csproj 文件中

<Project>
  ...
  <ItemGroup>
    <PackageReference Include="Opensearch.Net" Version="1.0.0" />
  </ItemGroup>
</Project>

连接到 OpenSearch

创建 OpenSearchLowLevelClient 对象时使用默认构造函数,以连接到默认的 OpenSearch 主机 (https://:9200)。

var client  = new OpenSearchLowLevelClient();

要通过已知地址的单个节点连接到 OpenSearch 集群,请创建一个包含该地址的 ConnectionConfiguration 对象,并将其传递给 OpenSearch.Net 构造函数

var nodeAddress = new Uri("http://myserver:9200");
var config = new ConnectionConfiguration(nodeAddress);
var client = new OpenSearchLowLevelClient(config);

您还可以使用连接池来管理集群中的节点。此外,您可以设置连接配置,使 OpenSearch 以格式化 JSON 的形式返回响应。

var uri = new Uri("https://:9200");
var connectionPool = new SingleNodeConnectionPool(uri);
var settings = new ConnectionConfiguration(connectionPool).PrettyJson();
var client = new OpenSearchLowLevelClient(settings);

要使用多个节点连接到 OpenSearch 集群,请创建包含这些节点地址的连接池。在此示例中,使用了SniffingConnectionPool,因为它会跟踪集群中被移除或添加的节点,因此最适合自动伸缩的集群。

var uris = new[]
{
    new Uri("https://:9200"),
    new Uri("https://:9201"),
    new Uri("https://:9202")
};
var connectionPool = new SniffingConnectionPool(uris);
var settings = new ConnectionConfiguration(connectionPool).PrettyJson();
var client = new OpenSearchLowLevelClient(settings);

连接到 Amazon OpenSearch 服务

以下示例演示了连接到 Amazon OpenSearch 服务

using OpenSearch.Client;
using OpenSearch.Net.Auth.AwsSigV4;

namespace Application
{
    class Program
    {
        static void Main(string[] args)
        {
            var endpoint = new Uri("https://search-xxx.region.es.amazonaws.com");
            var connection = new AwsSigV4HttpConnection(RegionEndpoint.APSoutheast2, service: AwsSigV4HttpConnection.OpenSearchService);
            var config = new ConnectionSettings(endpoint, connection);
            var client = new OpenSearchClient(config);

            Console.WriteLine($"{client.RootNodeInfo().Version.Distribution}: {client.RootNodeInfo().Version.Number}");
        }
    }
}

连接到 Amazon OpenSearch Serverless

以下示例演示了连接到 Amazon OpenSearch 无服务器服务

using OpenSearch.Client;
using OpenSearch.Net.Auth.AwsSigV4;

namespace Application
{
    class Program
    {
        static void Main(string[] args)
        {
            var endpoint = new Uri("https://search-xxx.region.aoss.amazonaws.com");
            var connection = new AwsSigV4HttpConnection(RegionEndpoint.APSoutheast2, service: AwsSigV4HttpConnection.OpenSearchServerlessService);
            var config = new ConnectionSettings(endpoint, connection);
            var client = new OpenSearchClient(config);

            Console.WriteLine($"{client.RootNodeInfo().Version.Distribution}: {client.RootNodeInfo().Version.Number}");
        }
    }
}

使用 ConnectionSettings

ConnectionConfiguration 用于将配置选项传递给 OpenSearch.Net 客户端。ConnectionSettings 继承自 ConnectionConfiguration 并提供额外的配置选项。以下示例使用 ConnectionSettings

  • 为未指定索引名称的请求设置默认索引名称。
  • 启用 gzip 压缩的请求和响应。
  • 向 OpenSearch 发送信号以返回格式化 JSON。
  • 将字段名称转换为小写。
var uri = new Uri("https://:9200");
var connectionPool = new SingleNodeConnectionPool(uri);
var settings = new ConnectionSettings(connectionPool)
    .DefaultIndex("students")
    .EnableHttpCompression()
    .PrettyJson()
    .DefaultFieldNameInferrer(f => f.ToLower());

var client = new OpenSearchLowLevelClient(settings);

索引单个文档

要索引文档,首先需要创建一个 Student 类实例

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

或者,您可以使用匿名类型创建一个 Student 实例

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

接下来,使用 Index 方法将此 Student 上传到 students 索引中

var response = client.Index<StringResponse>("students", "100", 
                                PostData.Serializable(student));
Console.WriteLine(response.Body);

Index 方法的泛型类型参数指定了响应正文类型。在上面的示例中,响应是一个字符串。

使用 Bulk API 索引多个文档

要索引多个文档,请使用 Bulk API 将多个操作捆绑到一个请求中

var studentArray = new object[]
{
    new {index = new { _index = "students", _type = "_doc", _id = "200"}},
    new {   Id = 200, 
            FirstName = "Shirley", 
            LastName = "Rodriguez", 
            Gpa = 3.91, 
            GradYear = 2019
    },
    new {index = new { _index = "students", _type = "_doc", _id = "300"}},
    new {   Id = 300, 
            FirstName = "Nikki", 
            LastName = "Wolf", 
            Gpa = 3.87, 
            GradYear = 2020
    }
};

var manyResponse = client.Bulk<StringResponse>(PostData.MultiJson(studentArray));

在接受请求正文的 API 中,您可以将请求正文作为匿名对象、字符串、字节数组或流发送。对于接受多行 JSON 的 API,您可以将正文作为字节列表或对象列表发送,如上例所示。PostData 类具有静态方法,可以以所有这些形式发送正文。

搜索文档

要构建 Query DSL 查询,请在请求正文中使用匿名类型。以下查询搜索所有在 2021 年毕业的学生

var searchResponseLow = client.Search<StringResponse>("students",
    PostData.Serializable(
    new
    {
        from = 0,
        size = 20,

        query = new
        {
            term = new
            {
                gradYear = new
                {
                    value = 2019
                }
            }
        }
    })); 

Console.WriteLine(searchResponseLow.Body);

或者,您可以使用字符串来构造请求。使用字符串时,您必须转义 " 字符

var searchResponse = client.Search<StringResponse>("students",
    @" {
    ""query"":
        {
            ""match"":
            {
                ""lastName"":
                {
                    ""query"": ""Santos""
                }
            }
        }
    }");

Console.WriteLine(searchResponse.Body);

异步使用 OpenSearch.Net 方法

对于需要异步代码的应用程序,OpenSearch.Client 中的所有方法调用都有对应的异步版本

// synchronous method
var response = client.Index<StringResponse>("students", "100", 
                                PostData.Serializable(student));

// asynchronous method
var response = client.IndexAsync<StringResponse>("students", "100", 
                                    PostData.Serializable(student));

处理异常

默认情况下,当操作不成功时,OpenSearch.Net 不会抛出异常。特别是,如果响应状态码是该请求的预期值之一,OpenSearch.Net 不会抛出异常。例如,以下查询在不存在的索引中搜索文档

var searchResponse = client.Search<StringResponse>("students1",
    @" {
    ""query"":
        {
            ""match"":
            {
                ""lastName"":
                {
                    ""query"": ""Santos""
                }
            }
        }
    }");

Console.WriteLine(searchResponse.Body);

响应包含错误状态码 404,这是搜索请求的预期错误代码之一,因此不会抛出异常。您可以在 status 字段中看到状态码

{
  "error" : {
    "root_cause" : [
      {
        "type" : "index_not_found_exception",
        "reason" : "no such index [students1]",
        "index" : "students1",
        "resource.id" : "students1",
        "resource.type" : "index_or_alias",
        "index_uuid" : "_na_"
      }
    ],
    "type" : "index_not_found_exception",
    "reason" : "no such index [students1]",
    "index" : "students1",
    "resource.id" : "students1",
    "resource.type" : "index_or_alias",
    "index_uuid" : "_na_"
  },
  "status" : 404
}

要配置 OpenSearch.Net 抛出异常,请在 ConnectionConfiguration 上启用 ThrowExceptions() 设置

var uri = new Uri("https://:9200");
var connectionPool = new SingleNodeConnectionPool(uri);
var settings = new ConnectionConfiguration(connectionPool)
                        .PrettyJson().ThrowExceptions();
var client = new OpenSearchLowLevelClient(settings);

您可以使用响应对象的以下属性来判断响应是否成功

Console.WriteLine("Success: " + searchResponse.Success);
Console.WriteLine("SuccessOrKnownError: " + searchResponse.SuccessOrKnownError);
Console.WriteLine("Original Exception: " + searchResponse.OriginalException);
  • Success 返回 true,如果响应代码在 2xx 范围内,或者响应代码是该请求的预期值之一。
  • SuccessOrKnownError 返回 true,如果响应成功,或者响应代码在 400–501 或 505–599 范围内。如果 SuccessOrKnownError 为 true,则不会重试请求。
  • OriginalException 保存了不成功响应的原始异常。

示例程序

以下程序创建索引、索引数据并搜索文档。

using OpenSearch.Net;
using OpenSearch.Client;

namespace NetClientProgram;

internal class Program
{
    public static void Main(string[] args)
    {
        // Create a client with custom settings
        var uri = new Uri("https://:9200");
        var connectionPool = new SingleNodeConnectionPool(uri);
        var settings = new ConnectionSettings(connectionPool)
            .PrettyJson();
        var client = new OpenSearchLowLevelClient(settings);


        Console.WriteLine("Indexing one student......");
        var student = new Student { 
            Id = 100, 
            FirstName = "Paulo", 
            LastName = "Santos", 
            Gpa = 3.93, 
            GradYear = 2021 };
        var response = client.Index<StringResponse>("students", "100",  
                                        PostData.Serializable(student));
        Console.WriteLine(response.Body);

        Console.WriteLine("Indexing many students......");
        var studentArray = new object[]
        {
            new { index = new { _index = "students", _type = "_doc", _id = "200"}},
            new {
                Id = 200, 
                FirstName = "Shirley", 
                LastName = "Rodriguez", 
                Gpa = 3.91, 
                GradYear = 2019},
            new { index = new { _index = "students", _type = "_doc", _id = "300"}},
            new {
                Id = 300, 
                FirstName = "Nikki", 
                LastName = "Wolf", 
                Gpa = 3.87, 
                GradYear = 2020}
        };

        var manyResponse = client.Bulk<StringResponse>(PostData.MultiJson(studentArray));

        Console.WriteLine(manyResponse.Body);


        Console.WriteLine("Searching for students who graduated in 2019......");
        var searchResponseLow = client.Search<StringResponse>("students",
            PostData.Serializable(
            new
            {
                from = 0,
                size = 20,

                query = new
                {
                    term = new
                    {
                        gradYear = new
                        {
                            value = 2019
                        }
                    }
                }
            }));

        Console.WriteLine(searchResponseLow.Body);

        Console.WriteLine("Searching for a student with the last name Santos......");

        var searchResponse = client.Search<StringResponse>("students",
            @" {
            ""query"":
                {
                    ""match"":
                    {
                        ""lastName"":
                        {
                            ""query"": ""Santos""
                        }
                    }
                }
            }");

        Console.WriteLine(searchResponse.Body);
    }
}