6

配列の最後の要素がある値に等しいドキュメントを見つけたいです。配列要素には、特定の配列位置からアクセスできます。

// i.e. comments[0].by == "Abe"
db.example.find( { "comments.0.by" : "Abe" } )

しかし、最後の項目を基準として使用して検索するにはどうすればよいですか?すなわち

db.example.find( { "comments.last.by" : "Abe" } )

ちなみに、私はphpを使用しています

4

5 に答える 5

11

私はこの質問が古いことを知っていますが、同様の新しい質問に答えた後、グーグルでそれを見つけました。だから私はこれが同じ治療に値すると思いました。

代わりにaggregateを使用することで、 $whereのパフォーマンスへの影響を回避できます。

db.example.aggregate([

    // Use an index, which $where cannot to narrow down
    {$match: { "comments.by": "Abe" }},

    // De-normalize the Array
    {$unwind: "$comments"},

    // The order of the array is maintained, so just look for the $last by _id
    {$group: { _id: "$_id", comments: {$last: "$comment"} }},

    // Match only where that $last comment by `by.Abe`
    {$match: { "comments.by": "Abe" }},

    // Retain the original _id order
    {$sort: { _id: 1 }}

])

そもそも「阿部」のコメントがあったドキュメントを絞り込むことができたので、$whereの周りでリングが鳴るはずです。警告されているように、$ whereはコレクション内のすべてのドキュメントをテストし、使用する必要がある場合でもインデックスを使用しないようにします。

もちろん、ここで説明する手法を使用して元のドキュメントを維持することもできるため、すべてがのように機能しfind()ます。

これを見つけた人のための単なる思考の糧。


最新のMongoDBリリースの更新

最近のリリースでは、$redactパイプライン式と$arrayElemAt(3.2の時点で後者なので、ここでは最小バージョンになります)を追加しました。これを組み合わせることで、論理式で$unwindステージを処理せずに配列の最後の要素を検査できます。

db.example.aggregate([
  { "$match": { "comments.by": "Abe" }},
  { "$redact": {
    "$cond": {
      "if": {
        "$eq": [
          { "$arrayElemAt": [ "$comments.by", -1 ] },
          "Abe"
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
])

ここでのロジック$arrayElemAtは、配列の最後のインデックスを取得する場所と比較して行われます。これは、を介してプロパティ-1内の値の配列のみに変換されます。これにより、単一の値を必要なパラメーターと比較できます。"by"$map"Abe"

$exprまたは、MongoDB 3.6以降の使用をもう少し現代的にします:

db.example.find({
  "comments.by": "Abe",
  "$expr": {
    "$eq": [
      { "$arrayElemAt": [ "$comments.by", -1 ] },
      "Abe"
    ]
  }
})

これは、配列内の最後の要素を照合するための最もパフォーマンスの高いソリューションであり、実際には$where、ほとんどの場合、特にここでの使用法に取って代わることが期待されます。

于 2014-02-22T11:21:07.703 に答える
7

このスキーマ設計では、これを一度に行うことはできません。長さを保存して2つのクエリを実行するか、最後のコメントを別のフィールドに追加で保存することができます。

{
    '_id': 'foo';
    'comments' [
        { 'value': 'comment #1', 'by': 'Ford' },
        { 'value': 'comment #2', 'by': 'Arthur' },
        { 'value': 'comment #3', 'by': 'Zaphod' }
    ],
    'last_comment': {
        'value': 'comment #3', 'by': 'Zaphod'
    }
}

もちろん、一部のデータを複製することになりますが、少なくとも、 forと$set一緒にこのデータを設定できます。$pushcomment

$comment = array(
    'value' => 'comment #3',
    'by' => 'Zaphod',
);

$collection->update(
    array( '_id' => 'foo' ),
    array(
        '$set' => array( 'last_comment' => $comment ),
        '$push' => array( 'comments' => $comment )
    )
);

最後のものを見つけるのは今簡単です!

于 2012-05-10T22:22:43.483 に答える
5

$whereこれは、演算子を使用して行うことができます。

db.example.find({ $where: 
    'this.comments.length && this.comments[this.comments.length-1].by === "Abe"' 
})

適用するための通常の低速パフォーマンスの警告$where"comments.by": "Abe"ただし、クエリに含めることでこれを支援できます。

db.example.find({
    "comments.by": "Abe",
    $where: 'this.comments.length && this.comments[this.comments.length-1].by === "Abe"' 
})

このように、$where阿部によるコメントを含む文書に対してのみ評価する必要があり、新しい用語はのインデックスを使用できるようになります"comments.by"

于 2013-01-05T01:23:14.613 に答える
3

私はただやっています:

db.products.find({'statusHistory.status':'AVAILABLE'},{'statusHistory': {$slice: -1}})

これにより、配列productsの最後の項目にプロパティが含まれていることがわかります。statusHistorystatus='AVAILABLE'

于 2019-05-09T11:58:02.993 に答える
0

上記の回答が削除された理由がわかりません。再投稿します。スキーマを変更しなくても、この方法で変更できるはずです。

db.example.find({ "comments:{$slice:-1}.by" : "Abe" } 

// ... また

db.example.find({ "comments.by" : "Abe" }

これはデフォルトで配列の最後の要素を取ります。

于 2013-01-04T23:17:52.990 に答える