sql >> データベース >  >> NoSQL >> MongoDB

配列内の一致する日付のクエリ

    $elemMatchがありません 基本クエリの演算子と $filter 集計フレームワークで試行した構文が実際には正しくありません。

    したがって、配列内のその範囲内にある日付に一致するドキュメントを返すのは次のとおりです。

    // Simulating the date values
    var start = new Date("2018-06-01"); // otherwise new Date(req.params.start)
    var end = new Date("2018-07-01");   // otherwise new Date(req.params.end)
    
    myColl.find({ 
      "_id": req.params.id,
      "someArray": {
        "$elemMatch": {  "$gte": start, "$lt": end  }
      }
    }).then( doc => {
      // do something with matched document
    }).catch(e => { console.err(e); res.send(e); })
    

    返される実際の配列要素のフィルタリングは次のとおりです。

    // Simulating the date values
    var start = new Date("2018-06-01");
    var end = new Date("2018-07-01");
    
    myColl.aggregate([
      { "$match": { 
        "_id": mongoose.Types.ObjectId(req.params.id),
        "someArray": {
          "$elemMatch": { "$gte": start, "$lt": end }
        }
      }},
      { "$project": {
        "name": 1,
        "someArray": {
          "$filter": {
            "input": "$someArray",
            "cond": {
              "$and": [
                { "$gte": [ "$$this.Timestamp", start ] }
                { "$lt": [ "$$this.Timestamp", end ] }
              ]
            }
          }
        }
      }}
    ]).then( docs => {
      /* remember aggregate returns an array always, so if you expect only one
       * then it's index 0
       *
       * But now the only items in 'someArray` are the matching ones, so you don't need 
       * the code you were writing to just pull out the matching ones
       */
       console.log(docs[0].someArray);
      
    }).catch(e => { console.err(e); res.send(e); })
    

    注意すべき点は、aggregate()にあることです。 ObjectIdを実際に「キャスト」する必要があります マングースの「自動キャスト」はここでは機能しないため、値です。通常、マングースはスキーマから読み取り、データのキャスト方法を決定しますが、集約パイプラインは「変更」を行うため、これは発生しません。

    $elemMatch ドキュメントにあるように

    つまり、 $gte および $lt AND条件であり、「2」としてカウントされるため、単純な「ドット表記」形式は適用されません。また、 $lt $lteではありません 、「最後のミリ秒」までの平等を探すよりも、「翌日」より「少ない」方が理にかなっているからです。

    $filter もちろん、その名前が示すとおりに正確に実行し、実際の配列コンテンツを「フィルタリング」して、一致するアイテムのみが残されるようにします。

    デモンストレーション

    完全なデモンストレーションリストは2つのドキュメントを作成し、1つは実際に日付範囲に一致する2つの配列アイテムのみを持ちます。最初のクエリは、正しいドキュメントが範囲と一致していることを示しています。 2つ目は、配列の「フィルタリング」を示しています。

    const { Schema, Types: { ObjectId } } = mongoose = require('mongoose');
    
    const uri = 'mongodb://localhost/test';
    
    mongoose.Promise = global.Promise;
    mongoose.set('debug',true);
    
    const subSchema = new Schema({
      timestamp: Date,
      other: String
    });
    
    const testSchema = new Schema({
      name: String,
      someArray: [subSchema]
    });
    
    const Test = mongoose.model('Test', testSchema, 'filtertest');
    
    const log = data => console.log(JSON.stringify(data, undefined, 2));
    
    const startDate = new Date("2018-06-01");
    const endDate = new Date("2018-07-01");
    
    (function() {
    
      mongoose.connect(uri)
        .then(conn =>
          Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()))
        )
        .then(() =>
          Test.insertMany([
            {
              _id: "5b1522f5cdac0b6da18f7618",
              name: 'A',
              someArray: [
                { timestamp: new Date("2018-06-01"), other: "C" },
                { timestamp: new Date("2018-07-04"), other: "D" },
                { timestamp: new Date("2018-06-10"), other: "E" }
              ]
            },
            {
              _id: "5b1522f5cdac0b6da18f761c",
              name: 'B',
              someArray: [
                { timestamp: new Date("2018-07-04"), other: "D" },
              ]
            }
          ])
        )
        .then(() =>
          Test.find({
            "someArray": {
              "$elemMatch": {
                "timestamp": { "$gte": startDate, "$lt": endDate }
              }
            }
          }).then(docs => log({ docs }))
        )
        .then(() =>
          Test.aggregate([
            { "$match": {
              "_id": ObjectId("5b1522f5cdac0b6da18f7618"),
              "someArray": {
                "$elemMatch": {
                  "timestamp": { "$gte": startDate, "$lt": endDate }
                }
              }
            }},
            { "$addFields": {
              "someArray": {
                "$filter": {
                  "input": "$someArray",
                  "cond": {
                    "$and": [
                      { "$gte": [ "$$this.timestamp", startDate ] },
                      { "$lt": [ "$$this.timestamp", endDate ] }
                    ]
                  }
                }
              }
            }}
          ]).then( filtered => log({ filtered }))
        )
        .catch(e => console.error(e))
        .then(() => mongoose.disconnect());
    
    })()
    

    または、async/awaitを使用してもう少し現代的な 構文:

    const { Schema, Types: { ObjectId } } = mongoose = require('mongoose');
    
    const uri = 'mongodb://localhost/test';
    
    mongoose.Promise = global.Promise;
    mongoose.set('debug',true);
    
    const subSchema = new Schema({
      timestamp: Date,
      other: String
    });
    
    const testSchema = new Schema({
      name: String,
      someArray: [subSchema]
    });
    
    const Test = mongoose.model('Test', testSchema, 'filtertest');
    
    const log = data => console.log(JSON.stringify(data, undefined, 2));
    
    (async function() {
    
      try {
    
        const startDate = new Date("2018-06-01");
        const endDate = new Date("2018-07-01");
    
        const conn = await mongoose.connect(uri);
    
        // Clean collections
        await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));
    
        // Create test items
    
        await Test.insertMany([
          {
            _id: "5b1522f5cdac0b6da18f7618",
            name: 'A',
            someArray: [
              { timestamp: new Date("2018-06-01"), other: "C" },
              { timestamp: new Date("2018-07-04"), other: "D" },
              { timestamp: new Date("2018-06-10"), other: "E" }
            ]
          },
          {
            _id: "5b1522f5cdac0b6da18f761c",
            name: 'B',
            someArray: [
              { timestamp: new Date("2018-07-04"), other: "D" },
            ]
          }
        ]);
    
    
    
        // Select matching 'documents'
        let docs = await Test.find({
          "someArray": {
            "$elemMatch": {
              "timestamp": { "$gte": startDate, "$lt": endDate }
            }
          }
        });
        log({ docs });
    
        let filtered = await Test.aggregate([
          { "$match": {
            "_id": ObjectId("5b1522f5cdac0b6da18f7618"),
            "someArray": {
              "$elemMatch": {
                "timestamp": { "$gte": startDate, "$lt": endDate }
              }
            }
          }},
          { "$addFields": {
            "someArray": {
              "$filter": {
                "input": "$someArray",
                "cond": {
                  "$and": [
                    { "$gte": [ "$$this.timestamp", startDate ] },
                    { "$lt": [ "$$this.timestamp", endDate ] }
                  ]
                }
              }
            }
          }}
        ]);
        log({ filtered });
    
        mongoose.disconnect();
    
      } catch(e) {
        console.error(e)
      } finally {
        process.exit()
      }
    
    })()
    

    どちらも同じで、同じ出力を提供します:

    Mongoose: filtertest.remove({}, {})
    Mongoose: filtertest.insertMany([ { _id: 5b1522f5cdac0b6da18f7618, name: 'A', someArray: [ { _id: 5b1526952794447083ababf6, timestamp: 2018-06-01T00:00:00.000Z, other: 'C' }, { _id: 5b1526952794447083ababf5, timestamp: 2018-07-04T00:00:00.000Z, other: 'D' }, { _id: 5b1526952794447083ababf4, timestamp: 2018-06-10T00:00:00.000Z, other: 'E' } ], __v: 0 }, { _id: 5b1522f5cdac0b6da18f761c, name: 'B', someArray: [ { _id: 5b1526952794447083ababf8, timestamp: 2018-07-04T00:00:00.000Z, other: 'D' } ], __v: 0 } ], {})
    Mongoose: filtertest.find({ someArray: { '$elemMatch': { timestamp: { '$gte': new Date("Fri, 01 Jun 2018 00:00:00 GMT"), '$lt': new Date("Sun, 01 Jul 2018 00:00:00 GMT") } } } }, { fields: {} })
    {
      "docs": [
        {
          "_id": "5b1522f5cdac0b6da18f7618",
          "name": "A",
          "someArray": [
            {
              "_id": "5b1526952794447083ababf6",
              "timestamp": "2018-06-01T00:00:00.000Z",
              "other": "C"
            },
            {
              "_id": "5b1526952794447083ababf5",
              "timestamp": "2018-07-04T00:00:00.000Z",
              "other": "D"
            },
            {
              "_id": "5b1526952794447083ababf4",
              "timestamp": "2018-06-10T00:00:00.000Z",
              "other": "E"
            }
          ],
          "__v": 0
        }
      ]
    }
    Mongoose: filtertest.aggregate([ { '$match': { _id: 5b1522f5cdac0b6da18f7618, someArray: { '$elemMatch': { timestamp: { '$gte': 2018-06-01T00:00:00.000Z, '$lt': 2018-07-01T00:00:00.000Z } } } } }, { '$addFields': { someArray: { '$filter': { input: '$someArray', cond: { '$and': [ { '$gte': [ '$$this.timestamp', 2018-06-01T00:00:00.000Z ] }, { '$lt': [ '$$this.timestamp', 2018-07-01T00:00:00.000Z ] } ] } } } } } ], {})
    {
      "filtered": [
        {
          "_id": "5b1522f5cdac0b6da18f7618",
          "name": "A",
          "someArray": [
            {
              "_id": "5b1526952794447083ababf6",
              "timestamp": "2018-06-01T00:00:00.000Z",
              "other": "C"
            },
            {
              "_id": "5b1526952794447083ababf4",
              "timestamp": "2018-06-10T00:00:00.000Z",
              "other": "E"
            }
          ],
          "__v": 0
        }
      ]
    }
    



    1. PHPでMongoDBforeachクエリをコーディングする方法

    2. SpringDataMongo-埋め込みドキュメントに一意の組み合わせフィールドを適用する

    3. mongodbの配列の先頭に値を追加するにはどうすればよいですか?

    4. MEANスタック:関数の結果をデータベースに更新する方法は?