LoginSignup
1
1

More than 5 years have passed since last update.

AzureCosmosDBでUDFを使ってjson arrayのPagenationとSort

Last updated at Posted at 2017-07-25

GYAOのtsです。
我々のチームは、オールパブリッククラウドで、Microservice Architectureを採用した次期バックエンドを設計中です。

経緯

前回の投稿で、Eventhubs経由で登録することができたので、あとは検索機能を作らないとということで、SortとPagenationのついたsearchAPIをAzureFunctionsで実装してみた。

やりたいこと

下記のようなデータがCosmosDBに保存されている

ID-user1
{
  "Contents": [
    {
      "ContentsId": "c0001",
      "Time": 1500532318
    },
    {
      "ContentsId": "c0006",
      "Time": 1500535647
    },
    {
      "ContentsId": "c0002",
      "Time": 1500538621
    }
  ],
  "Pkey": "reserve",
  "id": "user1"
}

このContents内をTime(unix timestamp)順にSortしたり、Pagenation(start, end)で絞ったりしたい。
AzureForumを見る限り、現時点ではTOPのサポートしかない模様・・・。これだとページネーションできない。
しかも今回は1ドキュメントのArrayになっているnestedオブジェクトのSortやPagenationなので、そもそも用途が違う気がする。。。

AzureCosmosDBのUDF(user defined functions)を利用して作ってみることにした。
udfで作成して、functionsで設定するSQLに埋め込むような形で行こうかと。

実装

UDF

まずはUDFの作成。
スクリーンショット 2017-07-25 15.20.04.png

sort
function sort(contents, start, end, sort) { 
  function compareTime(a, b) {
    if (sort == 'DESC') {
      return b.Time - a.Time;
    }
    else{
      return a.Time - b.Time;
    }
  }
  return contents.sort(compareTime).slice(start, end);
}

SQL

SQLには下記のような形で埋め込み、更にFunctionsの統合のsqlに設定する。
udfを使用する際はudf.[ID]でアクセスできる。

SQL
SELECT c.id, c.Pkey, udf.sort(c.Contents, {start}, {end}, {order}) as Contents FROM c where c.id = {docId}

Functions

下記の通り。

統合はこんな感じ
スクリーンショット 2017-07-25 15.22.56.png

http-inputのルートテンプレートで必要な引数をもらう設定
スクリーンショット 2017-07-25 15.24.02.png

そのまま引数に設定する。
気をつけるポイントは、inDocumentsをIEnumerable 型にすること。
SQLで取得する場合はID指定であっても複数ドキュメント限定らしい。

Functions
#r "Newtonsoft.Json"

using System;
using System.Net;
using Newtonsoft.Json;

public class Input
{
    public string docId { get; set; }
}

public static HttpResponseMessage Run(Input input, int start, int end, string order,
    HttpRequestMessage req, IEnumerable<dynamic> inputDocuments, TraceWriter log)
{
    var inputDocument = inputDocuments.First();
    log.Info($"invoked. start:{start}. limit:{end}. order:{order}");
    if (inputDocument != null)
    {
                var responseContent = JsonConvert.SerializeObject(inputDocument);
        var response = req.CreateResponse(HttpStatusCode.OK);
        response.Content = new StringContent(responseContent, Encoding.UTF8, "application/json");
        return response;
    }
    else
    {
        //not found. do nothing.
        return req.CreateResponse(HttpStatusCode.NotFound);
    }
}

以上。

Test

テストフォームが勝手に適用される。便利。。。
スクリーンショット 2017-07-25 15.28.11.png

取得完了。さくっと行けた。startとendだけだと利便性が悪いかもなので、limitとoffsetにしたほうがいいかも。

とりあえずプロトタイプはできた。

所感

パフォーマンス面に懸念は残るが、現時点ではこうするか、Functions内C#でSortの二択になりそうな気がする。
チーム内で相談した上で、今後ストレステスト等を経て決断していきたいと思う。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1