0

おおよそ次の形式のドキュメントを含むデータベースがあります。

{"created_at": some_datetime, "deleted_at": another_datetime, "foo": "bar"}

今後「deleted_at」を処理する必要がないと仮定すると、DB 内の削除されていないドキュメントの数を取得するのは簡単です。(UTC を使用して) 次のようなものに縮小するビューを作成することも簡単です。

[
  {"key": ["created", 2012, 7, 30], "value": 39},
  {"key": ["deleted", 2012, 7, 31], "value": 12}
  {"key": ["created", 2012, 8, 2], "value": 6}
]

...つまり、2012 年 7 月 30 日に 39 件のドキュメントが作成され、2012 年 7 月 31 日に 12 件が削除済みとしてマークされたということです。私が欲しいのは、2012 年 8 月 1 日 (0+39-12 == 27) に「存在した」ドキュメントの数のスナップショットを取得するための効率的なメカニズムです。理想的には、キーまたはインデックスとして日付を使用してビューまたは DB (たとえば、事前に計算されてディスクに保存されたもの) をクエリし、値またはドキュメントとしてカウントを取得できるようにしたいと考えています。例えば:

[
  {"key": [2012, 7, 30], "value": 39},
  {"key": [2012, 7, 31], "value": 27},
  {"key": [2012, 8,  1], "value": 27},
  {"key": [2012, 8,  2], "value": 33}
]

これは、ビュー内のすべての行を反復処理し、実行中のカウンターを保持し、毎日合計することで十分に簡単に計算できますが、データセットが大きくなるにつれて、そのアプローチは遅くなります。結果を保存します。これに取り組むよりスマートな方法はありますか?

4

2 に答える 2

0

比較のために(誰かがより良い解決策を持っていることを願っています)、現在(多かれ少なかれ)私が現在それをどのように解決しているか(未テストのルビー疑似コード)は次のとおりです。

require 'date'

def date_snapshots(rows)
  current_date  = nil
  current_count = 0
  rows.inject({}) {|hash, reduced_row|
    type, *ymd = reduced_row["key"]
    this_date  = Date.new(*ymd)
    if current_date
      # deal with the days where nothing changed
      (current_date.succ ... this_date).each do |date|
        key       = date.strftime("%Y-%m-%d")
        hash[key] = current_count
      end
    end
    # update the counter and deal with the current day
    current_date   = this_date
    current_count += reduced_row["value"] if type == "created_at"
    current_count -= reduced_row["value"] if type == "deleted_at"
    key       = current_date.strftime("%Y-%m-%d")
    hash[key] = current_count
    hash
  }
end

次に、次のように使用できます。

rows = couch_server.db(foo).design(bar).view(baz).reduce.group_level(3).rows
date_snapshots(rows)["2012-08-01"]

明らかな小さな改善は、キャッシング レイヤーを追加することですが、そのキャッシング レイヤーが適切に増分更新 (たとえば、変更フィード) を実行するようにすることはそれほど簡単ではありません。

于 2012-08-02T16:41:30.863 に答える
0

単一の日付のみを気にすると仮定すると、元のアプローチよりもはるかに優れていると思われるアプローチを見つけました。

def size_at(date=Time.now.to_date)
  ymd = [date.year, date.month, date.day]
  added = view.reduce.
    startkey(["created_at"]).
    endkey(  ["created_at", *ymd, {}]).rows.first || {}
  deleted = view.reduce.
    startkey(["deleted_at"]).
    endkey(  ["deleted_at", *ymd, {}]).rows.first || {}
  added.fetch("value", 0) - deleted.fetch("value", 0)
end

基本的に、リダクションは CouchDB に任せます。最初は、reduce と startkey/endkey を組み合わせて使用​​できるとは知りませんでした。

残念ながら、このアプローチでは DB への 2 つのヒットが必要です (ただし、これらは並列化またはパイプライン化できます)。また、一度に多くのサイズを取得したい場合 (たとえば、1 つの日付だけを見るのではなく、履歴全体を表示する場合) にはうまくいきません。

于 2012-08-02T18:18:14.620 に答える