3

次のドキュメント構造があると仮定します。

> db.logs.find()
{
'id': ObjectId("50ad8d451d41c8fc58000003")
'name': 'Sample Log 1',
'uploaded_at: ISODate("2013-03-14T01:00:00+01:00"),
'case_id: '50ad8d451d41c8fc58000099',
'tag_doc': {
  'group_x: ['TAG-1','TAG-2'],
  'group_y': ['XYZ']
}
},
{
'id': ObjectId("50ad8d451d41c8fc58000004")
'name': 'Sample Log 2',
'uploaded_at: ISODate("2013-03-15T01:00:00+01:00"),
'case_id: '50ad8d451d41c8fc58000099'
'tag_doc': {
  'group_x: ['TAG-1'],
  'group_y': ['XYZ']
}
}

> db.cases.findOne()
{
'id': ObjectId("50ad8d451d41c8fc58000099")
'name': 'Sample Case 1'
}

との一意の組み合わせごと$matchに最新のものだけを取得する集約フレームワークを実行する方法はありますか? これは複数のパイプラインで実行できると確信していますが、可能な限り、オペレーターを介してパイプラインを通過するドキュメントの数をすぐに制限したいと考えています。で使用される以外は、演算子のようなものを考えています。Logcase_idgroup_x$group$match$max$match

どんな助けでも大歓迎です。

編集:

これまでのところ、次のことを思いつくことができます。

db.logs.aggregate(
  {$match: {...}}, // some match filters here
  {$project: {tag:'$tag_doc.group_x', case:'$case_id', latest:{uploaded_at:1}}},
  {$unwind: '$tag'},
  {$group: {_id:{tag:'$tag', case:'$case'}, latest: {$max:'$latest'}}},
  {$group: {_id:'$_id.tag', total:{$sum:1}}}
)

前述したように、複数の$groupパイプラインを使用して目的を達成できますが、多数のドキュメントを処理する場合、これはコストがかかることがわかります。そういうわけで、できるだけ早くドキュメントを制限したかったのです。

編集:

私はまだ良い解決策を思い付いていないので、ドキュメント構造自体が私のユースケースに最適化されていないかどうかを考えています. 達成したいことをサポートするためにフィールドを更新する必要がありますか? 提案は大歓迎です。

編集:

私は実際にSQLで別の列によってMAX(列値)、DISTINCTで行を選択するにはどうすればよいですか?で期待されるものと同様のmongodbの実装を探しています ただし、2 つの異なるフィールド値が含まれます。また、$match一致するタグまたは日付の範囲内のフィルターを使用して、結果のセットが動的になるため、この操作は非常に重要です。

編集:

私のユースケースは複雑であるため、単純な類推を使用しようとしましたが、これは紛らわしいことがわかりました。上記は、実際のユースケースの簡略化された形式です。私が作成した混乱をお詫び申し上げます。

4

4 に答える 4

1

私は似たようなことをしました。ただし、一致では不可能ですが、1つのグループパイプラインでのみ可能です。秘訣は、正しい並べ替えでマルチキーを使用することです。

   { user_id: 1, address: "xyz", date_sent: ISODate("2013-03-14T01:00:00+01:00"), message: "test" }, { user_id: 1, address: "xyz2", date_sent: ISODate("2013-03-14T01:00:00+01:00"), message: "test" }

user_idとaddressでグループ化せず、最新の日付のメッセージを表示しない場合は、次のようなキーを作成する必要があります。

{ user_id:1, address:1, date_sent:-1 }

そうすれば、ソートなしで集計を実行できます。これははるかに高速で、レプリカを含むシャードで機能します。正しい並べ替え順序のキーがない場合は、並べ替えパイプラインを追加できますが、mongosに転送されてグループ化が行われるため、シャードで使用することはできません(メモリ制限の問題も発生します)

 db.user_messages.aggregate(
 { $match: { user_id:1 } },
 { $group: {
     _id: "$address",
     count: { $sum : 1 },
     date_sent: { $max : "$date_sent" },
     message: { $first : "$message" },
 } }
);

このように機能する必要があることは文書化されていませんが、実際に機能します。本番システムで使用しています。

于 2013-03-18T23:10:46.553 に答える
1

別のコレクションを使用して、新しいブログ投稿が投稿されるたびにこの新しいコレクション内のドキュメントをアップサートすることにより、新しい投稿が投稿されるとその場で検索結果を「作成」します。

作成者/タグの新しい組み合わせはすべて、このコレクションに新しいドキュメントとして追加されますが、既存の組み合わせを持つ新しい投稿は、新しいブログ投稿のコンテンツ (またはオブジェクト ID 参照) で既存のドキュメントを更新するだけです。

例:

db.searchResult.update(       
... {'author_id':'50ad8d451d41c8fc58000099', 'tag_doc.tags': ["TAG-1", "TAG-2" ]},
... { $set: { 'Referenceid':ObjectId("5152bc79e8bf3bc79a5a1dd8")}},  // or embed your blog post here
... {upsert:true}
)
于 2013-03-27T08:08:36.220 に答える
0

うーん、各作成者の最新のものだけを選択する必要があるような方法でこれを最適に行う良い方法はありません。代わりに、すべてのドキュメントを選択し、並べ替えてから、作成者でグループ化する必要があります。

db.posts.aggregate([
    {$sort: {created_at:-1}},
    {$group: {_id: '$author_id', tags: {$first: '$tag_doc.tags'}}},
    {$unwind: '$tags'},
    {$group: {_id: {author: '$_id', tag: '$tags'}}}
]);

あなたが言ったように、これは最適ではありませんが、私が思いついたのはそれだけです。

正直なところ、このクエリを頻繁に実行する必要がある場合は、次の形式で必要な情報が既に含まれている別のコレクションを事前に集計する方が実際には良いかもしれません。

{
    _id: {},
    author: {},
    tag: 'something',
    created_at: ISODate(),
    post_id: {}
}

そして、新しい投稿を作成するたびに、このユニークなコレクション内のすべてのドキュメントを探して、$in必要なもののクエリを満たし、更新/アップサートcreated_atpost_idてそのコレクションに追加します。これはより最適です。

于 2013-03-26T08:02:43.343 に答える