1

私は 2 つのコレクションを持っています。買い物客 (特定の日に店にいる全員) とビーチに行く人 (特定の日にビーチにいる全員) です。毎日のエントリーがあり、ビーチにいる人、買い物をしている人、両方をしている人、またはどちらもしていない人がいます。ここでクエリを実行したい - 過去 7 日間にビーチに行かなかったすべての買い物客。

私は Mongo を初めて使用するので、私のスキーマ設計が nosql DB には適していない可能性があります。join に関して同様の質問を見ましたが、ほとんどの場合、非正規化が提案されました。したがって、私が考えることができる1つの解決策は、コレクションを作成することです-アクティビティ、日付のインデックス、ユーザーのアクションの埋め込み。だから何か

{
   user_id
   date
   actions {
      [action_type, ..]
   }
}

挿入の前にクエリを実行する必要があるため、挿入にはコストがかかります。

4

3 に答える 3

3

いくつかの提案。

実行するすべてのクエリと、保存する必要があるすべての種類のデータを把握します。たとえば、将来アクティビティを追加する予定はありますか、それともビーチとショップだけですか?

書き込みと読み取りの数を検討し、どちらを高速にする必要があるかを検討してください。

スキーマが長期的にスケーラブルであることを確認するために、時間の経過とともにドキュメントがどのように成長するかを判断します。

これら 2 つのアクティビティしかない場合は、次の 1 つのアプローチが考えられます。ユーザーごとに 1 日 1 つのレコード。

{ user: "user1",
  date: "2012-12-01",
  shopped: 0,
  beached: 1
}

これで、アクティビティが 2 つであろうと 10 個であろうと、クエリがさらに簡単になります。

新しいアクティビティが発生すると、それに基づいて常に正しいレコードを更新する必要があります。ユーザー、日付、アクティビティを示すレコードをコレクションに追加するだけでよいと考えていた場合、挿入ははるかに高速ですが、クエリはユーザー、日付、およびアクティビティの両方をクエリする多くの作業を行う必要があります。

提案されたスキーマを使用した挿入/更新ステートメントは次のとおりです。

db.coll.update({"user":"username", "date": "somedate"}, {"shopped":{$inc:1}}, true)

それが言っていることは次のとおりです。「somedateのユーザー名について、shopped属性を1増やし、それが存在しない場合は作成します。別名「アップサート」(これが最後の「真の」引数です)。

これは、特定の日に activity1 を 2 回以上実行したが、activity2 をまったく実行しなかったすべてのユーザーに対するクエリです。

db.coll.find({"date":"somedate","shopped":0,"danced":{$gt:1}})

1 つのドキュメントが継続的かつ無制限に増加する可能性があるスキーマの選択には注意してください。

たとえば、日付とアクティビティの配列が増え続けるユーザー コレクションにすべてを格納すると、この問題が発生します。この説明については、ここで強調表示されているセクションを参照してください。また、大きなドキュメントが作業データ セットに取り込まれ続けることに注意してください。ドキュメントが巨大で、役に立たない (古い) データがたくさん含まれていると、ドキュメントのパフォーマンスが低下します。アプリケーション、およびディスク上のデータの断片化。

すべてのデータを 1 つのコレクションに入れる必要はありません。そのユーザーの属性の固定セットを持つユーザー コレクションを用意して、ユーザーの友人の数やその他の準安定情報を追跡し、ユーザーごとに毎日レコードを追加する user_activity コレクションを用意するのが最善の場合があります。彼らが行った活動。データの量または正規化または非正規化は、実行するクエリの種類と非常に密接に関連しています。

于 2012-06-03T19:18:56.137 に答える
0

挿入の前にクエリを実行する必要があるため、挿入にはコストがかかります。

RDBMS を使用しても、テーブルにインデックスが配置されている場合 (つまり、通常) は、挿入に (比較的) コストがかかる可能性があることに注意してください。Mongo で埋め込みドキュメントを使用することは、この点で大きな違いはないと思います。

クエリについては、Asya Kamsky が提案しているように、$nin 演算子を使用して、ビーチに行かなかったすべての人を見つけることができます。例えば:

db.people.find({ 
    actions: { $nin: ["beach"] }
});

ただし、この場合、埋め込みドキュメントを使用することはおそらく最善の方法ではありません。次のようなドキュメントを含む「フラットな」アクティビティ コレクションを用意するのが最善だと思います。

{
    user_id
    date
    action
}

次に、次のようなクエリを実行できます。

var start = new Date(2012, 6, 3);
var end = new Date(2012, 5, 27);
db.activities.find({ 
    date: {$gte: start, $lt: end }, 
    action: { $in: ["beach", "shopping" ] } 
});

最後のステップは、クライアント ドライバーで、"ショッピング" のレコードが存在し、"ビーチ" アクティビティのレコードが存在しないユーザー ID を見つけることです。

于 2012-06-03T19:14:57.487 に答える
0

考えられる構造の 1 つは、ドキュメントの埋め込み配列 ( usersコレクション)を使用することです。

{
    user_id: 1234,
    actions: [ 
        { action_type: "beach", date: "6/1/2012" },
        { action_type: "shopping", date: "6/2/2012" }
    ]
},
{ another user }

次に、 $elemMatchを使用して特定の基準 (この場合、過去 3 日間に買い物に行った人) に一致するユーザーを見つける、次のようなクエリを実行できます。

var start = new Date(2012, 6, 1);
db.people.find( { 
    actions : { 
        $elemMatch : { 
            action_type : { $in: ["shopping"] }, 
            date : { $gt : start } 
        } 
    } 
});

これを拡張すると、 $and 演算子を使用して、過去 3 日間に買い物に行ったものの、ビーチには行っていないすべての人を見つけることができます。

var start = new Date(2012, 6, 1);
db.people.find( {  
    $and: [
        actions : { 
            $elemMatch : { 
                action_type : { $in: ["shopping"] }, 
                date : { $gt : start } 
            } 
        },
        actions : { 
            $not: {
                $elemMatch : { 
                    action_type : { $in: ["beach"] }, 
                    date : { $gt : start } 
                } 
            }
        }
    ]
});
于 2012-06-03T19:38:13.267 に答える