본문 바로가기
WIKI 기술 지식 베이스

Low-level .NET client

원문 보기 위키 갱신

Low-level .NET client (OpenSearch.Net)

출처: 문서

본문

저수준 .NET 클라이언트 (OpenSearch.Net)

OpenSearch.Net은 OpenSearch와의 통신을 위한 기본 계층을 제공하는 저수준 .NET 클라이언트예요. 의존성이 없으며, 라운드 로빈 부하 분산, 전송, 기본 요청/응답 주기를 처리할 수 있어요. OpenSearch.Net은 모든 OpenSearch API 엔드포인트를 메서드로 포함하고 있어요. OpenSearch.Net을 사용할 때는 직접 쿼리를 구성해야 해요.

이 시작 가이드는 OpenSearch에 연결하고, 문서를 인덱싱하고, 쿼리를 실행하는 방법을 설명해요. 클라이언트 소스 코드는 opensearch-net 저장소를 참조하세요.

안정적인 릴리스

이 문서는 GitHub 저장소에서 제공하는 최신 업데이트를 반영하며, 현재 안정적인 릴리스에서는 사용할 수 없는 변경 사항이 포함될 수 있어요. NuGet의 현재 안정적인 릴리스는 2.2.0이에요. 지원되는 OpenSearch 버전 및 대상 프레임워크에 대한 자세한 내용은 호환성을 참조하세요.

OpenSearch.Net 클라이언트 설치하기

OpenSearch.Net을 설치하려면 OpenSearch.Net NuGet 패키지를 다운로드하여 원하는 IDE에서 프로젝트에 추가해요. Microsoft Visual Studio에서는 다음 단계를 따라 해요.

  • 솔루션 탐색기 패널에서 솔루션이나 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 솔루션에 대한 NuGet 패키지 관리를 선택해요.
  • OpenSearch.Net NuGet 패키지를 검색하고 설치를 선택해요.

또는 .NET CLI를 사용하여 프로젝트에 OpenSearch.Net을 추가할 수 있어요.

dotnet add package OpenSearch.Net --version 2.2.0

{% include copy.html %}

또한 .csproj 파일에 OpenSearch.Net을 추가할 수 있어요.

<Project>
  ...
  <ItemGroup>
    <PackageReference Include="OpenSearch.Net" Version="2.2.0" />
  </ItemGroup>
</Project>

{% include copy.html %}

샘플 데이터

이 페이지의 예제에서는 한 명의 학생을 나타내는 다음 Student 클래스를 사용해요. 이는 인덱스의 하나의 문서에 해당해요. ToString 메서드는 콘솔 출력을 위해 Student를 형식화해요.

using System.Globalization;
using System.Runtime.Serialization;

public class Student
{
    [DataMember(Name = "firstName")]
    public string FirstName { get; set; } = string.Empty;

    [DataMember(Name = "lastName")]
    public string LastName { get; set; } = string.Empty;

    [DataMember(Name = "gpa")]
    public double Gpa { get; set; }

    [DataMember(Name = "gradDate")]
    public string GradDate { get; set; } = string.Empty;

    public override string ToString() =>
        string.Format(CultureInfo.InvariantCulture,
            "{% raw %}Student{{firstName='{0}', lastName='{1}', gpa={2}, gradDate={3}}}{% endraw %}",
            FirstName, LastName, Gpa, GradDate);
}

{% include copy.html %}

기본적으로 OpenSearch.Net은 속성 이름을 선언된 그대로 직렬화해요. DataMember 특성은 필드 이름을 지정하므로 Student는 firstName, lastName, gpa, gradDate 필드를 포함하는 문서로 인덱싱돼요. {: .note}

OpenSearch에 연결하기

기본 OpenSearch 호스트(http://localhost:9200)에 연결하려면 OpenSearchLowLevelClient 객체를 생성할 때 기본 생성자를 사용해요.

var client  = new OpenSearchLowLevelClient();

{% include copy.html %}

알려진 주소를 가진 단일 노드를 통해 OpenSearch 클러스터에 연결하려면 해당 주소로 ConnectionConfiguration 객체를 생성하고 OpenSearch.Net 생성자에 전달해요.

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

{% include copy.html %}

또한 연결 풀을 사용하여 클러스터의 노드를 관리할 수 있어요. 추가로, OpenSearch가 응답을 형식화된 JSON으로 반환하도록 연결 구성을 설정할 수 있어요.

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

{% include copy.html %}

여러 노드를 사용하여 OpenSearch 클러스터에 연결하려면 해당 주소로 연결 풀을 생성해요. 이 예제에서는 SniffingConnectionPool을 사용해요. 이 풀은 클러스터에서 노드가 제거되거나 추가되는 것을 추적하므로 자동으로 확장되는 클러스터에 가장 적합해요.

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

{% include copy.html %}

Amazon OpenSearch Service에 연결하기

AWS Signature Version 4를 사용하여 Amazon OpenSearch Service에 요청을 서명하려면 OpenSearch.Net.Auth.AwsSigV4 패키지를 설치해요. 이 패키지는 OpenSearch.Net에 의존하므로 OpenSearch.Net도 함께 설치돼요.

dotnet add package OpenSearch.Net.Auth.AwsSigV4 --version 2.2.0

{% include copy.html %}

AwsSigV4HttpConnection은 기본 AWS 자격 증명 공급자 체인에서 자격 증명을 사용하여 요청을 서명해요. AwsSigV4HttpConnection에 전달하는 리전은 도메인 또는 컬렉션의 리전과 일치해야 해요. 다음 예제에서는 us-east-1 리전을 사용해요.

다음 예제에서는 엔드포인트를 Amazon OpenSearch Service 콘솔의 도메인 세부 정보 페이지에 나열된 도메인 엔드포인트로 바꿔요.

다음 예제는 Amazon OpenSearch Service에 연결하는 방법을 보여줘요.

using Amazon;
using OpenSearch.Net;
using OpenSearch.Net.Auth.AwsSigV4;

namespace Application
{
    class Program
    {
        static void Main(string[] args)
        {
            var endpoint = new Uri("https://search-<domain-name>-<id>.us-east-1.es.amazonaws.com");
            var connection = new AwsSigV4HttpConnection(RegionEndpoint.USEast1, service: AwsSigV4HttpConnection.OpenSearchService);
            var config = new ConnectionConfiguration(endpoint, connection);
            var client = new OpenSearchLowLevelClient(config);

            Console.WriteLine(client.RootNodeInfo<StringResponse>().Body);
        }
    }
}

{% include copy.html %}

Amazon OpenSearch Serverless에 연결하기

다음 예제는 Amazon OpenSearch Serverless에 연결하는 방법을 보여줘요. 엔드포인트를 Amazon OpenSearch Service 콘솔의 컬렉션 세부 정보 페이지에 나열된 컬렉션 엔드포인트로 바꿔요.

using Amazon;
using OpenSearch.Net;
using OpenSearch.Net.Auth.AwsSigV4;

namespace Application
{
    class Program
    {
        static void Main(string[] args)
        {
            var endpoint = new Uri("https://<collection-id>.us-east-1.aoss.amazonaws.com");
            var connection = new AwsSigV4HttpConnection(RegionEndpoint.USEast1, service: AwsSigV4HttpConnection.OpenSearchServerlessService);
            var config = new ConnectionConfiguration(endpoint, connection);
            var client = new OpenSearchLowLevelClient(config);

            Console.WriteLine(client.Cat.Indices<StringResponse>().Body);
        }
    }
}

{% include copy.html %}

Amazon OpenSearch Serverless는 OpenSearch API 작업의 하위 집합을 지원하며 이 페이지의 예제에서 사용된 refresh 매개변수를 지원하지 않아요. 자세한 내용은 Amazon OpenSearch Serverless의 지원되는 작업 및 플러그인을 참조하세요. {: .note}

ConnectionConfiguration 사용하기

ConnectionConfiguration을 사용하여 OpenSearch.Net 클라이언트에 구성 옵션을 전달해요. 다음 예제에서는 ConnectionConfiguration을 사용하여 다음을 수행해요.

  • gzip 압축 요청 및 응답을 활성화해요.
  • OpenSearch에 형식화된 JSON을 반환하도록 지시해요.
var uri = new Uri("http://localhost:9200");
var connectionPool = new SingleNodeConnectionPool(uri);
var settings = new ConnectionConfiguration(connectionPool)
    .EnableHttpCompression()
    .PrettyJson();

var client = new OpenSearchLowLevelClient(settings);

{% include copy.html %}

인덱스 생성하기

다음 예제는 하나의 기본 샤드와 하나의 복제본을 가진 인덱스를 생성해요. gradDate 필드를 yyyy-MM-dd 형식의 date로 명시적으로 매핑해요. OpenSearch는 문서를 인덱싱할 때 다른 문서 필드를 동적으로 매핑해요.

var index = "students";
var createIndexResponse = client.Indices.Create<DynamicResponse>(index,
    PostData.Serializable(new
    {
        settings = new
        {
            index = new
            {
                number_of_shards = 1,
                number_of_replicas = 1
            }
        },
        mappings = new
        {
            properties = new
            {
                gradDate = new { type = "date", format = "yyyy-MM-dd" }
            }
        }
    }));

{% include copy.html %}

각 메서드의 제네릭 형식 매개변수는 응답 형식을 지정해요. DynamicResponse를 사용하면 경로별로 응답 본문에서 값을 읽을 수 있어요(예: createIndexResponse.Get<string>("index")). StringResponse는 응답 본문을 문자열로 반환해요.

하나의 문서 인덱싱하기

문서를 인덱싱하려면 먼저 Student 클래스의 인스턴스를 생성해요.

var student = new Student { FirstName = "John", LastName = "Doe", Gpa = 3.89, GradDate = "2022-05-15" };

{% include copy.html %}

또는 익명 형식을 사용하여 학생을 생성할 수 있어요. 이 경우 속성 이름이 필드 이름이 돼요.

var student = new { firstName = "John", lastName = "Doe", gpa = 3.89, gradDate = "2022-05-15" };

{% include copy.html %}

다음으로 Index 메서드를 사용하여 이 학생을 ID 1로 students 인덱스에 업로드해요. Refresh를 Refresh.True로 설정하면 문서를 즉시 검색할 수 있어요.

var indexResponse = client.Index<DynamicResponse>(index, "1",
    PostData.Serializable(student),
    new IndexRequestParameters { Refresh = Refresh.True });

{% include copy.html %}

Bulk API를 사용하여 여러 문서 인덱싱하기

여러 문서를 인덱싱하려면 Bulk API를 사용하여 여러 작업을 하나의 요청으로 묶어요.

var bulkBody = new object[]
{
    new { index = new { _index = index, _id = "2" } },
    new Student { FirstName = "Paulo", LastName = "Santos", Gpa = 3.93, GradDate = "2021-05-20" },
    new { index = new { _index = index, _id = "3" } },
    new Student { FirstName = "Shirley", LastName = "Rodriguez", Gpa = 3.91, GradDate = "2019-05-10" }
};
var bulkResponse = client.Bulk<StringResponse>(PostData.MultiJson(bulkBody),
    new BulkRequestParameters { Refresh = Refresh.True });

{% include copy.html %}

본문을 받는 API에서는 요청 본문을 익명 객체, 문자열, 바이트 배열 또는 스트림으로 보낼 수 있어요. 여러 줄 JSON을 받는 API의 경우 앞의 예제처럼 본문을 바이트 목록 또는 객체 목록으로 보낼 수 있어요. PostData 클래스에는 이러한 모든 형식으로 본문을 보내는 정적 메서드가 있어요.

문서 검색하기

Query DSL 쿼리를 구성하려면 요청 본문 내에서 익명 형식을 사용해요. 다음 쿼리는 모든 학생을 검색해요.

var searchResponse = client.Search<StringResponse>(index,
    PostData.Serializable(new { query = new { match_all = new { } } }));
Console.WriteLine(searchResponse.Body);

{% include copy.html %}

다음 범위 쿼리는 2019년에 졸업한 학생을 검색해요.

var searchResponse = client.Search<StringResponse>(index,
    PostData.Serializable(new
    {
        query = new
        {
            range = new
            {
                gradDate = new { gte = "2019-01-01", lte = "2019-12-31" }
            }
        }
    }));
Console.WriteLine(searchResponse.Body);

{% include copy.html %}

또는 문자열을 사용하여 요청을 구성할 수 있어요. 문자열을 사용할 때는 " 문자를 이스케이프해야 해요.

var searchResponse = client.Search<StringResponse>(index,
    @" {
    ""query"":
        {
            ""range"":
            {
                ""gradDate"":
                {
                    ""gte"": ""2019-01-01"",
                    ""lte"": ""2019-12-31""
                }
            }
        }
    }");
Console.WriteLine(searchResponse.Body);

{% include copy.html %}

결과 페이지 매기기

결과를 페이지로 나누려면 from 및 size 매개변수를 사용해요. 다음 예제는 학생을 졸업 날짜별로 정렬하고 결과를 한 번에 두 개씩 검색해요. 첫 번째 요청은 첫 번째 페이지 결과를 반환하고, 두 번째 요청은 다음 페이지를 반환해요.

var firstPageResponse = client.Search<StringResponse>(index,
    PostData.Serializable(new
    {
        from = 0,
        size = 2,
        sort = new[] { new { gradDate = "asc" } },
        query = new { match_all = new { } }
    }));
Console.WriteLine(firstPageResponse.Body);

var nextPageResponse = client.Search<StringResponse>(index,
    PostData.Serializable(new
    {
        from = 2,
        size = 2,
        sort = new[] { new { gradDate = "asc" } },
        query = new { match_all = new { } }
    }));
Console.WriteLine(nextPageResponse.Body);

{% include copy.html %}

from 및 size 매개변수는 결과의 처음 몇 페이지에 잘 작동해요. 많은 수의 결과를 페이지로 나누려면 search_after와 함께 포인트 인 타임을 사용해요. 자세한 내용은 결과 페이지 매기기를 참조하세요.

문서 업데이트하기

doc 필드에 부분 문서를 보내서 문서를 업데이트해요. 부분 문서에 있는 필드만 업데이트돼요.

var updateResponse = client.Update<DynamicResponse>(index, "1",
    PostData.Serializable(new { doc = new { gpa = 3.92 } }));

{% include copy.html %}

문서 삭제하기

다음 코드를 사용하여 문서를 삭제해요.

var deleteResponse = client.Delete<DynamicResponse>(index, "3",
    new DeleteRequestParameters { Refresh = Refresh.True });

{% include copy.html %}

인덱스 삭제하기

다음 코드를 사용하여 인덱스를 삭제해요.

var deleteIndexResponse = client.Indices.Delete<DynamicResponse>(index);

{% include copy.html %}

OpenSearch.Net 메서드를 비동기로 사용하기

비동기 코드가 필요한 애플리케이션의 경우 OpenSearch.Net의 모든 메서드 호출에는 비동기 대응 메서드가 있어요.

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

// asynchronous method
var asyncResponse = await client.IndexAsync<StringResponse>(index, "1",
                                    PostData.Serializable(student));

{% include copy.html %}

예외 처리하기

기본적으로 OpenSearch.Net은 작업이 실패해도 예외를 throw하지 않아요. 예를 들어 다음 쿼리는 존재하지 않는 인덱스에서 문서를 검색해요.

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

Console.WriteLine(searchResponse.Body);

{% include copy.html %}

응답에는 404 오류 상태 코드가 포함되지만 예외는 throw되지 않아요. 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이 예외를 throw하도록 구성하려면 ConnectionConfiguration에서 ThrowExceptions() 설정을 켜요.

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

{% include copy.html %}

요청이 성공했는지 확인하려면 응답 객체의 다음 속성을 사용해요.

Console.WriteLine("Success: " + searchResponse.Success);
Console.WriteLine("SuccessOrKnownError: " + searchResponse.SuccessOrKnownError);
Console.WriteLine("Original Exception: " + searchResponse.OriginalException);

{% include copy.html %}

  • Success는 응답 코드가 2xx 범위에 있거나 응답 코드가 이 요청에 대해 예상된 값 중 하나인 경우 true를 반환해요.
  • SuccessOrKnownError는 응답이 성공적이거나 응답 코드가 400–501 또는 505–599 범위에 있는 경우 true를 반환해요. SuccessOrKnownError가 true이면 요청이 재시도되지 않아요.
  • OriginalException은 실패한 응답에 대한 원래 예외를 보유해요.

샘플 프로그램

이 샘플 프로그램은 앞의 섹션의 코드를 결합해요. Security 플러그인이 활성화된 클러스터에 연결해요. Security 플러그인 없이 클러스터에 연결하려면 // Without security 주석으로 표시된 줄을 변경해요. 샘플 프로그램을 실행하기 전에 프로젝트에 Student 클래스가 정의되어 있는지 확인해요. 반환된 문서를 출력하기 위해 샘플 프로그램은 System.Text.Json을 사용하여 각 문서의 _source를 Student로 역직렬화해요.

이 샘플 프로그램은 테스트 전용이에요. 코드에 자격 증명을 지정하고 자체 서명된 인증서를 사용하는 클러스터에 연결할 수 있도록 인증서 유효성 검사를 비활성화해요. 프로덕션에서는 안전한 위치에서 자격 증명을 로드하고 클러스터의 인증서를 검증해요. {: .warning}

다음 샘플 프로그램은 클라이언트를 생성하고, 인덱스를 생성하고, 문서를 개별적으로 및 대량으로 인덱싱하고, 문서를 검색하고, 문서를 업데이트하고, 문서를 삭제한 다음 인덱스를 삭제해요.

using System.Text.Json;
using OpenSearch.Net;

namespace NetClientProgram;

internal class Program
{
    public static void Main(string[] args)
    {
        var config = new ConnectionConfiguration(new Uri("https://localhost:9200")); // Without security, use http://localhost:9200
        config.BasicAuthentication("admin", "<custom-admin-password>"); // Without security, remove this line
        config.ServerCertificateValidationCallback(CertificateValidations.AllowAll); // Without security, remove this line
        var client = new OpenSearchLowLevelClient(config);

        // Create the index
        var index = "students";
        Console.WriteLine("Creating index......");
        var createIndexResponse = client.Indices.Create<DynamicResponse>(index,
            PostData.Serializable(new
            {
                settings = new
                {
                    index = new
                    {
                        number_of_shards = 1,
                        number_of_replicas = 1
                    }
                },
                mappings = new
                {
                    properties = new
                    {
                        gradDate = new { type = "date", format = "yyyy-MM-dd" }
                    }
                }
            }));
        Console.WriteLine("Index created: " + createIndexResponse.Get<string>("index"));

        // Index a document
        Console.WriteLine("\nIndexing one student......");
        var student = new Student { FirstName = "John", LastName = "Doe", Gpa = 3.89, GradDate = "2022-05-15" };
        var indexResponse = client.Index<DynamicResponse>(index, "1",
            PostData.Serializable(student),
            new IndexRequestParameters { Refresh = Refresh.True });
        Console.WriteLine($"Result: {indexResponse.Get<string>("result")}, id: {indexResponse.Get<string>("_id")}, version: {indexResponse.Get<long>("_version")}");

        // Bulk index documents
        Console.WriteLine("\nIndexing many students......");
        var bulkBody = new object[]
        {
            new { index = new { _index = index, _id = "2" } },
            new Student { FirstName = "Paulo", LastName = "Santos", Gpa = 3.93, GradDate = "2021-05-20" },
            new { index = new { _index = index, _id = "3" } },
            new Student { FirstName = "Shirley", LastName = "Rodriguez", Gpa = 3.91, GradDate = "2019-05-10" }
        };
        var bulkResponse = client.Bulk<StringResponse>(PostData.MultiJson(bulkBody),
            new BulkRequestParameters { Refresh = Refresh.True });
        using (var bulkJson = JsonDocument.Parse(bulkResponse.Body))
        {
            Console.WriteLine("Errors: " + bulkJson.RootElement.GetProperty("errors").GetRawText());
            foreach (var item in bulkJson.RootElement.GetProperty("items").EnumerateArray())
            {
                var operation = item.GetProperty("index");
                Console.WriteLine($"  {operation.GetProperty("result")} id: {operation.GetProperty("_id")}");
            }
        }

        // Search for all students
        Console.WriteLine("\nSearching for all students......");
        var searchResponse = client.Search<StringResponse>(index,
            PostData.Serializable(new
            {
                from = 0,
                size = 2,
                sort = new[] { new { gradDate = "asc" } },
                query = new { match_all = new { } }
            }));
        PrintTotal(searchResponse);
        Console.WriteLine("Page 1:");
        PrintStudents(searchResponse);

        var nextPageResponse = client.Search<StringResponse>(index,
            PostData.Serializable(new
            {
                from = 2,
                size = 2,
                sort = new[] { new { gradDate = "asc" } },
                query = new { match_all = new { } }
            }));
        Console.WriteLine("Page 2:");
        PrintStudents(nextPageResponse);

        // Search for students who graduated in 2019
        Console.WriteLine("\nSearching for students who graduated in 2019......");
        var searchResponse2 = client.Search<StringResponse>(index,
            PostData.Serializable(new
            {
                query = new
                {
                    range = new
                    {
                        gradDate = new { gte = "2019-01-01", lte = "2019-12-31" }
                    }
                }
            }));
        PrintTotal(searchResponse2);
        PrintStudents(searchResponse2);

        // Update a document
        Console.WriteLine("\nUpdating a student's GPA......");
        var updateResponse = client.Update<DynamicResponse>(index, "1",
            PostData.Serializable(new { doc = new { gpa = 3.92 } }));
        Console.WriteLine($"Result: {updateResponse.Get<string>("result")}, version: {updateResponse.Get<long>("_version")}");

        // Get the updated document
        var getResponse = client.Get<StringResponse>(index, "1");
        using (var getJson = JsonDocument.Parse(getResponse.Body))
        {
            var updatedStudent = getJson.RootElement.GetProperty("_source").Deserialize<Student>(JsonOptions);
            Console.WriteLine("Updated document: " + updatedStudent);
        }

        // Delete a document
        Console.WriteLine("\nDeleting a student......");
        var deleteResponse = client.Delete<DynamicResponse>(index, "3",
            new DeleteRequestParameters { Refresh = Refresh.True });
        Console.WriteLine("Result: " + deleteResponse.Get<string>("result"));

        // Delete the index
        Console.WriteLine("\nDeleting the index......");
        var deleteIndexResponse = client.Indices.Delete<StringResponse>(index);
        using (var deleteIndexJson = JsonDocument.Parse(deleteIndexResponse.Body))
        {
            Console.WriteLine("Acknowledged: " + deleteIndexJson.RootElement.GetProperty("acknowledged").GetRawText());
        }
    }

    // Deserializes camelCase JSON field names into Student properties
    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);

    // Prints the total number of hits
    private static void PrintTotal(StringResponse searchResponse)
    {
        using var json = JsonDocument.Parse(searchResponse.Body);
        var hits = json.RootElement.GetProperty("hits");
        Console.WriteLine("Total hits: " + hits.GetProperty("total").GetProperty("value"));
    }

    // Prints the student in each hit
    private static void PrintStudents(StringResponse searchResponse)
    {
        using var json = JsonDocument.Parse(searchResponse.Body);
        var hits = json.RootElement.GetProperty("hits");
        foreach (var hit in hits.GetProperty("hits").EnumerateArray())
        {
            var student = hit.GetProperty("_source").Deserialize<Student>(JsonOptions);
            Console.WriteLine("  " + student);
        }
    }
}

{% include copy.html %}

샘플 프로그램은 다음 출력을 생성해요.

Creating index......
Index created: students

Indexing one student......
Result: created, id: 1, version: 1

Indexing many students......
Errors: false
  created id: 2
  created id: 3

Searching for all students......
Total hits: 3
Page 1:
  Student{firstName='Shirley', lastName='Rodriguez', gpa=3.91, gradDate=2019-05-10}
  Student{firstName='Paulo', lastName='Santos', gpa=3.93, gradDate=2021-05-20}
Page 2:
  Student{firstName='John', lastName='Doe', gpa=3.89, gradDate=2022-05-15}

Searching for students who graduated in 2019......
Total hits: 1
  Student{firstName='Shirley', lastName='Rodriguez', gpa=3.91, gradDate=2019-05-10}

Updating a student's GPA......
Result: updated, version: 2
Updated document: Student{firstName='John', lastName='Doe', gpa=3.92, gradDate=2022-05-15}

Deleting a student......
Result: deleted

Deleting the index......
Acknowledged: true

관련 문서

더 알아보기 (Learn more)