0

ドキュメントは以下のようなものです。

{
    "title": "Book1",
    "dailyactiviescores":[
        {
            "date": 2013-06-05,
            "score": 10,
        },
        {
            "date": 2013-06-06,
            "score": 21,
        },
    ]
}

1 日のアクティブ スコアは、読者が本を開くと増加することを意図しています。最初に頭に浮かぶ解決策は、「$」を使用して、対象の日付にスコアがあるかどうかを調べ、それを処理することです。

err = bookCollection.Update(
    {"title":"Book1", "dailyactivescore.date": 2013-06-06},
    {"$inc":{"dailyactivescore.$.score": 1}})
if err == ErrNotFound {
    bookCollection.Update({"title":"Book1"}, {"$push":...})
}

しかし、配列内の項目のインデックスを返す方法はあるのでしょうか? その場合、2 つではなく 1 つのクエリを使用して作業を行うことができます。このような。

index = bookCollection.Find(
    {"title":"Book1", "dailyactivescore.date": 2013-06-06}).Select({"$index"})
if index != -1 {
    incTarget = FormatString("dailyactivescore.%d.score", index)
    bookCollection.Update(..., {"$inc": {incTarget: 1}})
} else {
    //push here
}
4

2 に答える 2

2

存在しないフィールドをインクリメントすることは問題ではありません。その上で $inc:1 を実行すると、フィールドが作成され、ポストインクリメントが 1 に設定されるだけだからです。問題は、インクリメントする日付に対応する配列項目がない場合です。

ここにはいくつかの可能な解決策があります (インクリメントするための複数のステップを必要としません)。

1 つは、次のように、配列要素内のすべての日付を score:0 で事前に作成することです。

{
    "title": "Book1",
    "dailyactiviescores":[
        {
            "date": 2013-06-01,
            "score": 0,
        },
        {
            "date": 2013-06-02,
            "score": 0,
        },
        {
            "date": 2013-06-03,
            "score": 0,
        },
        {
            "date": 2013-06-04,
            "score": 0,
        },
        {
            "date": 2013-06-05,
            "score": 0,
        },
        {
            "date": 2013-06-06,
            "score": 0
        },  { etc ... }
    ]
}

しかし、将来はどこまで行くのでしょうか?したがって、ここでの 1 つのオプションは「バケット」です。たとえば、活動ドキュメントを「月ごと」に作成し、月の開始前に翌月の新しいドキュメントを作成するジョブを作成します。少しヤバい。しかし、それはうまくいくでしょう。

その他のオプションには、スキーマのわずかな変更が含まれます。

book、date、activity_scores のコレクションを使用できます。次に、単純な upsert を使用してスコアをインクリメントできます。

   db.books.update({title:"Book1", date:"2013-06-02", {$inc:{score:1}}, {upsert:true})

これにより、スコアがインクリメントされるか、この本と日付のスコア:1 の新しいレコードが挿入され、コレクションは次のようになります。

{
    "title": "Book1",
    "date": 2013-06-01,
    "score": 10,
},
{
    "title": "Book1",
    "date": 2013-06-02,
    "score": 1,
}, ...

実際のユースケースから例をどれだけ単純化したかに応じて、これはうまくいくかもしれません。

別のオプションは、配列に固執することですが、インクリメントするキーとして日付文字列を使用することに切り替えます。

スキーマ:

{
    "title": "Book1",
    "dailyactiviescores":{
            { "2013-06-01":10},
            { "2013-06-02":8}
    }
 }

配列ではなくサブドキュメントになっていることに注意してください。次のことができます。

db.books.update({title:"Book1"}, {"dailyactivityscores.2013-06-03":{$inc:1}})

サブドキュメントに新しい日付を追加してインクリメントすると、次のようになります。

{
    "title": "Book1",
    "dailyactiviescores":{
            { "2013-06-01":10},
            { "2013-06-02":8},
            { "2013-06-03":1}
    }
 }

書籍のスコアを「合計」するのが難しくなったため、同じ update ステートメントで「小計」をアトミックに更新することもできます。

しかし、ここでもまた、このサブドキュメントに日数を追加し続けるのは問題があります。数年後にまだ残っていて、これらの本のドキュメントが非常に大きくなった場合はどうなるでしょうか?

過去 N 日間のアクティビティ スコアのみを保持する場合を除き (2.4 の capped array 機能を使用してこれを行うことができます)、ブック アクティビティ スコアを追跡するための個別のコレクションを作成する方が簡単になると思います。各日のスコアを本のコレクションの本に埋め込むのではなく、別のドキュメントを作成します。

于 2013-06-05T13:16:50.877 に答える
1

ドキュメントによると:

$inc 演算子は、指定された量だけフィールドの値を増やします。フィールドが存在しない場合、$inc はフィールドを指定された金額に設定します。

したがって、score配列項目にフィールドがない$inc場合は、次のように 1 に設定されます。

{
    "title": "Book1",
    "dailyactiviescores":[
        {
            "date": 2013-06-05,
            "score": 10,
        },
        {
            "date": 2013-06-06,
        },
    ]
}

bookCollection.Update(
    {"title":"Book1", "dailyactivescore.date": 2013-06-06},
    {"$inc":{"dailyactivescore.$.score": 1}})

結果は次のようになります。

{
    "title": "Book1",
    "dailyactiviescores":[
        {
            "date": 2013-06-05,
            "score": 10,
        },
        {
            "date": 2013-06-06,
            "score": 1
        },
    ]
}

それが役立つことを願っています。

于 2013-06-05T12:15:59.760 に答える