테이블 및 인덱스 쿼리: .NET - Amazon DynamoDB

테이블 및 인덱스 쿼리: .NET

Query 작업을 사용하면 Amazon DynamoDB에서 테이블이나 보조 인덱스를 쿼리할 수 있습니다. 파티션 키 값과 등식 조건을 입력해야 합니다. 테이블 또는 인덱스에 정렬 키가 있는 경우 정렬 키 값과 조건을 제공하여 결과를 구체화할 수 있습니다.

다음은 하위 수준 AWS SDK for .NET API를 사용하여 테이블을 쿼리하는 단계입니다.

  1. AmazonDynamoDBClient 클래스의 인스턴스를 만듭니다.

  2. QueryRequest 클래스의 인스턴스를 만들고 쿼리 작업 파라미터를 제공합니다.

  3. Query 메서드를 실행하고 이전 단계에서 생성한 QueryRequest 객체를 입력합니다.

    응답에는 쿼리에서 반환한 모든 항목을 제공하는 QueryResult 객체가 포함되어 있습니다.

다음은 위에서 설명한 작업을 실행하는 C# 코드 예제입니다. 예를 들어, 포럼 스레드에 대한 댓글을 저장하는 Reply 테이블이 있다고 가정합니다. 자세한 내용은 DynamoDB에서 테이블 생성 및 코드 예제에 대한 데이터 로드 섹션을 참조하세요.

Reply Id, ReplyDateTime, ... )

각 포럼 스레드에는 고유 ID가 있으며 회신 수는 0개 이상일 수 있습니다. 따라서 기본 키도 Id(파티션 키)와 ReplyDateTime(정렬 키), 두 가지로 구성됩니다.

다음 쿼리에서는 특정 스레드 주제에 대한 모든 회신을 검색합니다. 이 쿼리를 실행하려면 테이블 이름과 Subject 값을 모두 입력해야 합니다.

AmazonDynamoDBClient client = new AmazonDynamoDBClient(); var request = new QueryRequest { TableName = "Reply", KeyConditionExpression = "Id = :v_Id", ExpressionAttributeValues = new Dictionary<string, AttributeValue> { {":v_Id", new AttributeValue { S = "Amazon DynamoDB#DynamoDB Thread 1" }}} }; var response = client.Query(request); foreach (Dictionary<string, AttributeValue> item in response.Items) { // Process the result. PrintItem(item); }

옵션 파라미터 지정

Query 메서드는 여러 가지 선택적 파라미터를 지원합니다. 예를 들어 조건을 지정하여 이전 쿼리의 쿼리 결과를 필요에 따라 좁혀 과거 2주간의 회신을 반환할 수 있습니다. 이 조건을 정렬 키 조건이라고 합니다. DynamoDB에서 기본 키의 정렬 키에 대해 지정하는 쿼리 조건을 평가하기 때문입니다. 다른 선택적 파라미터를 지정하여 쿼리 결과의 항목에서 특정 속성 목록만 가져올 수 있습니다. 자세한 내용은 쿼리를 참조하세요.

다음 C# 코드 예제는 지난 15일간 게시된 포럼 스레드 댓글을 검색합니다. 이 예제에서 지정하는 옵션 파라미터는 다음과 같습니다.

  • KeyConditionExpression은 과거 15일간의 응답만 가져옵니다.

  • ProjectionExpression 파라미터는 속성 목록을 지정하여 쿼리 결과의 항목을 가져옵니다.

  • ConsistentRead 파라미터는 강력한 일관된 읽기(Strongly Consistent Read)를 수행합니다.

DateTime twoWeeksAgoDate = DateTime.UtcNow - TimeSpan.FromDays(15); string twoWeeksAgoString = twoWeeksAgoDate.ToString(AWSSDKUtils.ISO8601DateFormat); var request = new QueryRequest { TableName = "Reply", KeyConditionExpression = "Id = :v_Id and ReplyDateTime > :v_twoWeeksAgo", ExpressionAttributeValues = new Dictionary<string, AttributeValue> { {":v_Id", new AttributeValue { S = "Amazon DynamoDB#DynamoDB Thread 2" }}, {":v_twoWeeksAgo", new AttributeValue { S = twoWeeksAgoString }} }, ProjectionExpression = "Subject, ReplyDateTime, PostedBy", ConsistentRead = true }; var response = client.Query(request); foreach (Dictionary<string, AttributeValue> item in response.Items) { // Process the result. PrintItem(item); }

또한 필요할 경우 옵션으로 Limit 파라미터를 추가하여 페이지 크기 또는 페이지당 항목 수를 제한할 수 있습니다. Query 메서드를 실행할 때마다 지정된 수의 항목이 있는 1페이지의 결과를 가져옵니다. 다음 페이지를 가져오려면 이전 페이지의 마지막 항목의 기본 키 값을 제공하여 Query 메서드가 다음 항목 집합을 반환할 수 있도록 이 메서드를 다시 실행합니다. ExclusiveStartKey 속성을 설정하여 요청 시 이 정보를 제공합니다. 이 속성의 초기 값은 null이 될 수 있습니다. 연이어 다음 페이지까지 가져오려면 이 속성 값을 이전 페이지에서 마지막 항목의 기본 키로 업데이트해야 합니다.

다음 C# 예제는 Reply 테이블을 쿼리합니다. 요청에서 옵션으로 LimitExclusiveStartKey 파라미터를 지정합니다. do/while 루프는 LastEvaluatedKey가 null 값을 반환할 때까지 계속해서 한 페이지를 스캔합니다.

Dictionary<string,AttributeValue> lastKeyEvaluated = null; do { var request = new QueryRequest { TableName = "Reply", KeyConditionExpression = "Id = :v_Id", ExpressionAttributeValues = new Dictionary<string, AttributeValue> { {":v_Id", new AttributeValue { S = "Amazon DynamoDB#DynamoDB Thread 2" }} }, // Optional parameters. Limit = 1, ExclusiveStartKey = lastKeyEvaluated }; var response = client.Query(request); // Process the query result. foreach (Dictionary<string, AttributeValue> item in response.Items) { PrintItem(item); } lastKeyEvaluated = response.LastEvaluatedKey; } while (lastKeyEvaluated != null && lastKeyEvaluated.Count != 0);

예 - AWS SDK for .NET을 사용하여 쿼리

다음 테이블에는 포럼 모음에 대한 정보가 저장됩니다. 자세한 내용은 DynamoDB에서 테이블 생성 및 코드 예제에 대한 데이터 로드 섹션을 참조하세요.

Forum ( Name, ... ) Thread ( ForumName, Subject, Message, LastPostedBy, LastPostDateTime, ...) Reply ( Id, ReplyDateTime, Message, PostedBy, ...)

이 예제에서는 포럼 "DynamoDB"의 "DynamoDB Thread 1" 스레드에 대한 회신 검색의 변형 작업을 실행합니다.

  • 스레드에 대한 회신을 찾습니다.

  • 스레드에 대한 회신을 찾습니다. Limit 쿼리 파라미터를 지정하여 페이지 크기를 설정합니다.

    이 함수는 페이지 매김을 사용한 여러 페이지 결과 처리를 보여 줍니다. DynamoDB에는 페이지 크기 제한이 있으며 결과가 페이지 크기를 초과하는 경우 첫 페이지 결과만 가져옵니다. 이 코딩 패턴에서는 코드가 쿼리 결과의 모든 페이지를 처리합니다.

  • 지난 15일 간에 해당하는 회신을 찾습니다.

  • 특정 날짜 범위에 해당하는 회신을 찾습니다.

    앞서 다룬 두 쿼리 모두 정렬 키 조건을 지정하여 쿼리 결과를 좁히고 여러 선택적 쿼리 파라미터를 사용하는 방법을 보여 줍니다.

다음 예제를 테스트하기 위한 단계별 지침은 .NET 코드 예제 단원을 참조하세요.

/** * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * This file is licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. A copy of * the License is located at * * http://aws.amazon.com/apache2.0/ * * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ using System; using System.Collections.Generic; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; using Amazon.Runtime; using Amazon.Util; namespace com.amazonaws.codesamples { class LowLevelQuery { private static AmazonDynamoDBClient client = new AmazonDynamoDBClient(); static void Main(string[] args) { try { // Query a specific forum and thread. string forumName = "Amazon DynamoDB"; string threadSubject = "DynamoDB Thread 1"; FindRepliesForAThread(forumName, threadSubject); FindRepliesForAThreadSpecifyOptionalLimit(forumName, threadSubject); FindRepliesInLast15DaysWithConfig(forumName, threadSubject); FindRepliesPostedWithinTimePeriod(forumName, threadSubject); Console.WriteLine("Example complete. To continue, press Enter"); Console.ReadLine(); } catch (AmazonDynamoDBException e) { Console.WriteLine(e.Message); Console.ReadLine(); } catch (AmazonServiceException e) { Console.WriteLine(e.Message); Console.ReadLine(); } catch (Exception e) { Console.WriteLine(e.Message); Console.ReadLine(); } } private static void FindRepliesPostedWithinTimePeriod(string forumName, string threadSubject) { Console.WriteLine("*** Executing FindRepliesPostedWithinTimePeriod() ***"); string replyId = forumName + "#" + threadSubject; // You must provide date value based on your test data. DateTime startDate = DateTime.UtcNow - TimeSpan.FromDays(21); string start = startDate.ToString(AWSSDKUtils.ISO8601DateFormat); // You provide date value based on your test data. DateTime endDate = DateTime.UtcNow - TimeSpan.FromDays(5); string end = endDate.ToString(AWSSDKUtils.ISO8601DateFormat); var request = new QueryRequest { TableName = "Reply", ReturnConsumedCapacity = "TOTAL", KeyConditionExpression = "Id = :v_replyId and ReplyDateTime between :v_start and :v_end", ExpressionAttributeValues = new Dictionary<string, AttributeValue> { {":v_replyId", new AttributeValue { S = replyId }}, {":v_start", new AttributeValue { S = start }}, {":v_end", new AttributeValue { S = end }} } }; var response = client.Query(request); Console.WriteLine("\nNo. of reads used (by query in FindRepliesPostedWithinTimePeriod) {0}", response.ConsumedCapacity.CapacityUnits); foreach (Dictionary<string, AttributeValue> item in response.Items) { PrintItem(item); } Console.WriteLine("To continue, press Enter"); Console.ReadLine(); } private static void FindRepliesInLast15DaysWithConfig(string forumName, string threadSubject) { Console.WriteLine("*** Executing FindRepliesInLast15DaysWithConfig() ***"); string replyId = forumName + "#" + threadSubject; DateTime twoWeeksAgoDate = DateTime.UtcNow - TimeSpan.FromDays(15); string twoWeeksAgoString = twoWeeksAgoDate.ToString(AWSSDKUtils.ISO8601DateFormat); var request = new QueryRequest { TableName = "Reply", ReturnConsumedCapacity = "TOTAL", KeyConditionExpression = "Id = :v_replyId and ReplyDateTime > :v_interval", ExpressionAttributeValues = new Dictionary<string, AttributeValue> { {":v_replyId", new AttributeValue { S = replyId }}, {":v_interval", new AttributeValue { S = twoWeeksAgoString }} }, // Optional parameter. ProjectionExpression = "Id, ReplyDateTime, PostedBy", // Optional parameter. ConsistentRead = true }; var response = client.Query(request); Console.WriteLine("No. of reads used (by query in FindRepliesInLast15DaysWithConfig) {0}", response.ConsumedCapacity.CapacityUnits); foreach (Dictionary<string, AttributeValue> item in response.Items) { PrintItem(item); } Console.WriteLine("To continue, press Enter"); Console.ReadLine(); } private static void FindRepliesForAThreadSpecifyOptionalLimit(string forumName, string threadSubject) { Console.WriteLine("*** Executing FindRepliesForAThreadSpecifyOptionalLimit() ***"); string replyId = forumName + "#" + threadSubject; Dictionary<string, AttributeValue> lastKeyEvaluated = null; do { var request = new QueryRequest { TableName = "Reply", ReturnConsumedCapacity = "TOTAL", KeyConditionExpression = "Id = :v_replyId", ExpressionAttributeValues = new Dictionary<string, AttributeValue> { {":v_replyId", new AttributeValue { S = replyId }} }, Limit = 2, // The Reply table has only a few sample items. So the page size is smaller. ExclusiveStartKey = lastKeyEvaluated }; var response = client.Query(request); Console.WriteLine("No. of reads used (by query in FindRepliesForAThreadSpecifyLimit) {0}\n", response.ConsumedCapacity.CapacityUnits); foreach (Dictionary<string, AttributeValue> item in response.Items) { PrintItem(item); } lastKeyEvaluated = response.LastEvaluatedKey; } while (lastKeyEvaluated != null && lastKeyEvaluated.Count != 0); Console.WriteLine("To continue, press Enter"); Console.ReadLine(); } private static void FindRepliesForAThread(string forumName, string threadSubject) { Console.WriteLine("*** Executing FindRepliesForAThread() ***"); string replyId = forumName + "#" + threadSubject; var request = new QueryRequest { TableName = "Reply", ReturnConsumedCapacity = "TOTAL", KeyConditionExpression = "Id = :v_replyId", ExpressionAttributeValues = new Dictionary<string, AttributeValue> { {":v_replyId", new AttributeValue { S = replyId }} } }; var response = client.Query(request); Console.WriteLine("No. of reads used (by query in FindRepliesForAThread) {0}\n", response.ConsumedCapacity.CapacityUnits); foreach (Dictionary<string, AttributeValue> item in response.Items) { PrintItem(item); } Console.WriteLine("To continue, press Enter"); Console.ReadLine(); } private static void PrintItem( Dictionary<string, AttributeValue> attributeList) { foreach (KeyValuePair<string, AttributeValue> kvp in attributeList) { string attributeName = kvp.Key; AttributeValue value = kvp.Value; Console.WriteLine( attributeName + " " + (value.S == null ? "" : "S=[" + value.S + "]") + (value.N == null ? "" : "N=[" + value.N + "]") + (value.SS == null ? "" : "SS=[" + string.Join(",", value.SS.ToArray()) + "]") + (value.NS == null ? "" : "NS=[" + string.Join(",", value.NS.ToArray()) + "]") ); } Console.WriteLine("************************************************"); } } }