我有具有以下结构的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);