C# で Amazon DynamoDB に保存されている時系列データを GSI(グローバルセカンダリインデックス)を使って範囲指定して検索する方法です。
[事前準備1] データ
下記の構造のデータを保存できるよう、
[DynamoDBTable("SampleTable")]
public class Sample
{
/// <summary>「SampleTable」テーブルのハッシュキー</summary>
[DynamoDBHashKey]
public string Id { get; set; }
/// <summary>「SampleTable」テーブルのレンジキー</summary>
[DynamoDBRangeKey]
public string DateAndTime { get; set; }
/// <summary>「Date-Time-index」GSI のハッシュキー</summary>
[DynamoDBGlobalSecondaryIndexHashKey]
public string Date { get; set; }
/// <summary>「Date-Time-index」GSI のレンジキー</summary>
[DynamoDBGlobalSecondaryIndexRangeKey]
public string Time { get; set; }
[DynamoDBProperty]
public string Message { get; set; }
public override string ToString()
{
return $"{Id} / {DateAndTime} / {Message}";
}
}
AWS Console から Id
、DateAndTime
をキーにした SampleTable
テーブルを用意します。
次に、下記のように SaveAsync
メソッドを使って 10件のデータを作成します。
// DynamoDB にアクセスするためのオブジェクトを用意
using var client = new AmazonDynamoDBClient(RegionEndpoint.APNortheast1);
using var context = new DynamoDBContext(client);
// 10件のデータを作成
for (var i = 0; i < 10; i++)
{
var now = DateTime.Now;
await context.SaveAsync(new Sample
{
Id = $"{i % 3 + 1:D3}",
DateAndTime = $"{now:yyyy-MM-dd HH:mm:ss.fff}",
Date = $"{now:yyyy-MM-dd}",
Time = $"{now:HH:mm:ss}",
Message = $"メッセージ - {i + 1}"
});
// 保存される時間をずらしたいので 500 msec 待機
await Task.Delay(500);
}
下記のように ScanAsync
メソッドを使って全データを取得します。
// 全データを取得
var listAll = await context.ScanAsync<Sample>(null).GetRemainingAsync();
Console.WriteLine("--- listAll");
listAll.OrderBy(n => n.DateAndTime).ToList().ForEach(Console.WriteLine);
そうすると、下記のようにデータを取得できます。
--- listAll
001 / 2020-08-11 11:40:40.236 / メッセージ - 1
002 / 2020-08-11 11:40:41.224 / メッセージ - 2
003 / 2020-08-11 11:40:41.756 / メッセージ - 3
001 / 2020-08-11 11:40:42.293 / メッセージ - 4
002 / 2020-08-11 11:40:42.824 / メッセージ - 5
003 / 2020-08-11 11:40:43.355 / メッセージ - 6
001 / 2020-08-11 11:40:43.877 / メッセージ - 7
002 / 2020-08-11 11:40:44.412 / メッセージ - 8
003 / 2020-08-11 11:40:44.936 / メッセージ - 9
001 / 2020-08-11 11:40:45.458 / メッセージ - 10
[事前準備2] GSI
AWS Console からは下記のようにデータが保存されていることを確認できます。
事前準備の最後に、日時を範囲指定して検索できるよう、AWS Console から Date
、Time
をキーにした Date-Time-index
インデックス(GSI)を用意します。
これで準備は完了です。
[シナリオ1] Id = 001 のデータを取得したい
下記のようにハッシュキーに 001
を指定して QueryAsync()
メソッドを使ってデータを取得します。
var list01 = await context.QueryAsync<Sample>("001").GetRemainingAsync();
Console.WriteLine("--- list01");
list01.OrderBy(n => n.DateAndTime).ToList().ForEach(Console.WriteLine);
そうすると、下記のようにデータを取得できます。
--- list01
001 / 2020-08-11 11:40:40.236 / メッセージ - 1
001 / 2020-08-11 11:40:42.293 / メッセージ - 4
001 / 2020-08-11 11:40:43.877 / メッセージ - 7
001 / 2020-08-11 11:40:45.458 / メッセージ - 10
Id が 001 のデータのみを取得できています。
[シナリオ2] 2020-08-11 の 11:40:42~11:40:43 のデータを取得したい
下記のように GSI の名称 Date-Time-index
を指定し、ハッシュキーに 2020-08-11
を指定して、レンジキーには QueryOperator.Between
と 11:40:42 - 11:40:43
を指定して QueryAsync()
メソッドを使ってデータを取得します。
var list02 = await context.QueryAsync<Sample>(
"2020-08-11",
QueryOperator.Between,
new List<object>
{
"11:40:42",
"11:40:43"
},
new DynamoDBOperationConfig
{
IndexName = "Date-Time-index" // 利用する GSI を指定
}).GetRemainingAsync();
Console.WriteLine("--- list02");
list02.OrderBy(n => n.DateAndTime).ToList().ForEach(Console.WriteLine);
そうすると、下記のようにデータを取得できます。
--- list02
001 / 2020-08-11 11:40:42.293 / メッセージ - 4
002 / 2020-08-11 11:40:42.824 / メッセージ - 5
003 / 2020-08-11 11:40:43.355 / メッセージ - 6
001 / 2020-08-11 11:40:43.877 / メッセージ - 7
SampleTable
テーブルのハッシュキーである Id
をまたいで 11:40:42 - 11:40:43
のデータのみを取得できています。
さいごに
GSI を使って日時を範囲指定して検索することはできました。
ただ、コスト面の考慮や NoSQL らしい設計についてはできているかというと、、
より良い方法があったら教えてください m(_ _)m