Mongodb子文档搜索和选择



我有具有以下结构的mongo数据库:

集合结构

我需要在时间范围内获取父文档及其子文档。我正在使用nodejs mongoosejs驱动程序。这是我当前的要求:

    var searchQueryTerminal = TickModel
    .findOne({
        symbol:symbol,
        'tickData.timestamp':{
            $gte:dateStart,
            $lte:dateEnd
        },
        broker:{$in: broker},
        MTVersion:MTVersion
    });

正如预期的那样,我在结果中有 1 个父文档,但它包含所有子文档,而不仅仅是在时间范围内。如何使用猫鼬驱动程序以正确的方式做到这一点?我真的不想在节点端这样做,因为子文档的数量可能是数百万。

模式:

var TickDataSchema   = new mongoose.Schema({
bid: Number,
ask: Number,
timestamp: {
    type:Date,
    default:Date.now,
    expires: '7d'
}});

var TickSchema   = new mongoose.Schema({
    broker:{
        type: String,
        index:true
    },
    account: Number,
    symbol: {
        type: String,
        index:true
    },
    MTVersion:Number,
    key: {
    type:String,
        index:true
    },
    tickData:[TickDataSchema]
});

使用猫鼬实现 $elemMatch。在 mongo 中使用以下集合进行了测试:

db.test.insert([
    { "symbol": "AMBC", "MTVersion": 0.25, "tickData": [{ "bid": 1, "ask": 1.5, "timestamp": ISODate("2015-01-28T08:00:00.000Z") }, { "bid": 1, "ask": 1.3, "timestamp": ISODate("2015-01-21T08:00:00.000Z") }] },
    { "symbol": "MSFT", "MTVersion": 0.25, "tickData": [{ "bid": 1, "ask": 1.5, "timestamp": ISODate("2015-02-04T08:00:00.000Z") }, { "bid": 1, "ask": 1.3, timestamp: ISODate("2015-02-11T08:00:00.000Z") }] }
]);
var symbol = "AMBC",
    dateStart = new Date (2015, 0, 20),
    dateEnd = new Date(2015, 0, 22),
    MTVersion = 0.25;
db.test.findOne({
    "symbol": symbol,
    "tickData.timestamp": {
        "$gte": dateStart,
        "$lte": dateEnd
    },    
    "MTVersion": MTVersion
}, {
    "symbol": 1,
    "MTVersion": 1,
    "tickData": {
        "$elemMatch": {
            "timestamp": {
                "$gte": dateStart,
                "$lte": dateEnd
            }
        }
    }
});

退货

/* 0 */
{
    "_id" : ObjectId("5530f8c4180e849972938fdf"),
    "symbol" : "AMBC",
    "MTVersion" : 0.25,
    "tickData" : [ 
        {
            "bid" : 1,
            "ask" : 1.3,
            "timestamp" : ISODate("2015-01-21T08:00:00.000Z")
        }
    ]
}

所以在猫鼬中,你可以做这样的事情:

TickModel
    .findOne({
        symbol: symbol,
        broker: {$in: broker},
        MTVersion: MTVersion
    })
    .elemMatch("tickData", { "timestamp": {"$gte": dateStart, "$lte": dateEnd } })
    // .select({"tickData": { "$elemMatch": { "timestamp": {"$gte": dateStart, "$lte": dateEnd } }, "symbol": 1, "MTVersion": 1, "broker": 1 })
    .exec(cb);

使用聚合框架,这可以通过以下管道实现:

var pipeline = [
    {
        "$match": {
            "symbol": symbol,
            "tickData.timestamp": {
                "$gte": dateStart,
                "$lte": dateEnd
            },    
            "MTVersion": MTVersion
        }
    },
    {
        "$unwind": "$tickData"
    },
    {
        "$match": {            
            "tickData.timestamp": {
                "$gte": dateStart,
                "$lte": dateEnd
            }
        }
    },
    {
        "$group": {
            _id: {
                "id": "$_id",
                "broker": "$broker",
                "account": "$account"
                "symbol": "$symbol",
                "MTVersion": "$MTVersion",
                "key": "$key"
            },
            "tickData": {
                "$addToSet": "$tickData"
            }
        }
    },
    {
        "$project": {
            "_id": 0,
            "tickData": 1,
            "broker": "$_id.broker",
            "account": "$_id.account"
            "symbol": "$_id.symbol",
            "MTVersion": "$_id.MTVersion",
            "key": "$_id.key"
        }
    }
];
TickModel.aggregate(pipeline, function (err, result) {
        if (err) {
            console.log(err);
            return;
        }
        console.log(result);
    });

结果:(使用上述示例文档)

/* 0 */
{
    "result" : [ 
        {
            "tickData" : [ 
                {
                    "bid" : 1,
                    "ask" : 1.3,
                    "timestamp" : ISODate("2015-01-21T08:00:00.000Z")
                }
            ],
            "symbol" : "AMBC",
            "MTVersion" : 0.25
        }
    ],
    "ok" : 1
}

您可以尝试填充函数

 var searchQueryTerminal = TickModel
    .findOne({
        symbol:symbol,
        broker:{$in: broker},
        MTVersion:MTVersion
    }).populate({
        path: 'tickData',
        match: { timestamp: { $gte:dateStart, $lte:dateEnd}},
    })

来自 vkarpov15 的答案

这是因为猫鼬的.elemMatch()函数是MongoDB的$elemMatch查询运算符的助手,而不是$elemMatch投影运算符。请参阅elemMatch上的猫鼬文档。诚然,两者之间的区别是棘手的。

您可以使用我给您的显式表单或 .select() 帮助程序:

TickModel.
  findOne({
    "symbol": 'EURUSD',
    "tickData.timestamp": {
        "$gte": ISODate("2015-04-17T10:06:00.000Z"),
        "$lte": ISODate("2015-04-17T10:10:00.000Z")
    },    
    "MTVersion": 4
  }).
  select(
    "tickData": {
        "$elemMatch": {
            "timestamp": {
                "$gte": ISODate("2015-04-17T10:06:00.000Z"),
                "$lte": ISODate("2015-04-17T10:10:00.000Z")
            }
        }
    }
  }).exec(callback);

最新更新