我有以下型号
var messageSchema = new Schema({
creationDate: { type: Date, default: Date.now },
comment: { type: String },
author: { type: Schema.Types.ObjectId }
});
var conversationSchema = new Schema({
title: { type: String },
author: { type : Schema.Types.ObjectId },
members: [ { type: Schema.Types.ObjectId } ],
creationDate: { type: Date, default: Date.now },
lastUpdate: { type: Date, default: Date.now },
comments: [ messageSchema ]
});
我想创建两个方法来获取由用户或对话ID在日期之后生成的注释。
按用户
我尝试了以下方法
var query = {
members : { $all : [ userId, otherUserId ], "$size" : 2 }
, comments : { $elemMatch : { creationDate : { $gte: from } } }
};
如果在指定日期之后(自)起没有注释,则该方法返回[]或null
通过对话ID
当我尝试通过用户ID获取时,也会发生同样的情况
var query = { _id : conversationId
, comments : { $elemMatch : { creationDate : { $gte: from } } }
};
有什么方法可以使该方法返回带有空注释的对话信息?
谢谢!
在这里听起来像是几个问题,但逐步解决所有这些问题
为了从数组中获得不止一个匹配“或”,需要mapReduce的聚合框架来执行此操作。您可以尝试使用“投射”,$elemMatch
但这只能返回“第一个”匹配。IE:
{ "a": [1,2,3] }
db.collection.find({ },{ "$elemMatch": { "$gte": 2 } })
{ "a": [2] }
因此,标准投影对此不起作用。它可以返回一个“空”数组,但也只能返回匹配的“第一个”数组。
继续前进,您的代码中也包含以下内容:
{ $all : [ userId, otherUserId ], "$site" : 2 }
哪里$site
不是有效的运算符。我认为您的意思是,$size
但是实际上有两个“两个”运算符具有该名称,您的意图在这里可能不清楚。
如果您表示要测试的数组必须具有“仅两个”元素,则这是适合您的运算符。如果您的意思是两个人之间匹配的对话必须在比赛中相等,那么$all
无论如何$size
都要这样做,因此在任何情况下都变得多余,除非您不希望对话中的任何其他人。
关于聚合问题。您需要以“非破坏性方式”“过滤”数组的内容,以获取多个匹配项或一个空数组。
最好的方法是使用2.6中提供的现代MongoDB功能,该功能允许对数组内容进行过滤而不进行处理$unwind
:
Model.aggregate(
[
{ "$match": {
"members": { "$all": [userId,otherUserId] }
}},
{ "$project": {
"title": 1,
"author": 1,
"members": 1,
"creationDate": 1,
"lastUpdate": 1,
"comments": {
"$setDifference": [
{ "$map": {
"input": "$comments",
"as": "c",
"in": { "$cond": [
{ "$gte": [ "$$c.creationDate", from ] },
"$$c",
false
]}
}},
[false]
]
}
}}
],
function(err,result) {
}
);
那使用$map
可以针对每个数组元素处理表达式。在这种情况下,值在$cond
三元数下进行测试,以返回条件所在的数组元素,true
或者返回false
为元素。
然后由$setDifference
运算符对它们进行“过滤”,运算符实际上将的结果数组$map
与另一个数组进行比较[false]
。这false
将从结果数组中删除所有值,并且仅保留匹配的元素或根本不保留任何元素。
可能有一个替代方法,$redact
但是由于您的文档在多个级别上包含“ creationDate”,因此这与$$DESCEND
操作符使用的逻辑混淆。这排除了该行动。
在早期版本中,“不破坏”阵列需要谨慎对待。因此,您需要对结果执行相同的“过滤器”操作,以获得所需的“空”数组:
Model.aggregate(
[
{ "$match": {
"$and": [
{ "members": userId },
{ "members": otherUserId }
}},
{ "$unwind": "$comments" },
{ "$group": {
"_id": "$_id",
"title": { "$first": "$title" },
"author": { "$first": "$author" },
"members": { "$first": "$members" },
"creationDate": { "$first": "$creationDate" },
"lastUpdate": { "$first": "$lastUpdate" },
"comments": {
"$addToSet": {
"$cond": [
{ "$gte": [ "$comments.creationDate", from ] },
"$comments",
false
]
}
},
"matchedSize": {
"$sum": {
"$cond": [
{ "$gte": [ "$comments.creationDate", from ] },
1,
0
]
}
}
}},
{ "$unwind": "$comments" },
{ "$match": {
"$or": [
{ "comments": { "$ne": false } },
{ "matchedSize": 0 }
]
}},
{ "$group": {
"_id": "$_id",
"title": { "$first": "$title" },
"author": { "$first": "$author" },
"members": { "$first": "$members" },
"creationDate": { "$first": "$creationDate" },
"lastUpdate": { "$first": "$lastUpdate" },
"comments": { "$push": "$comments" }
}},
{ "$project": {
"title": 1,
"author": 1,
"members": 1,
"creationDate": 1,
"lastUpdate": 1,
"comments": {
"$cond": [
{ "$eq": [ "$comments", [false] ] },
{ "$const": [] },
"$comments"
]
}
}}
],
function(err,result) {
}
)
这可以做很多相同的事情,但是需要更长的时间。为了查看数组内容,您需要$unwind
该内容。当您$group
返回时,您将查看每个元素以查看其是否符合条件以决定要返回的内容,同时还要保留匹配项的计数。
这是要放些(一个$addToSet
)false
结果数组或只是一个数组与条目在false
那里有没有比赛。因此,您$match
可以使用匹配的“计数”过滤掉这些,并进行测试,以查看是否未找到匹配项。如果找不到匹配项,则不会丢弃该项目。
相反,您可以[false]
在final中将数组替换为空数组$project
。
因此,根据您的MongoDB版本,这是“快速/容易”或“缓慢/困难”的处理。令人信服的理由来更新已经存在多年的版本。
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/aggtest');
var memberSchema = new Schema({
name: { type: String }
});
var messageSchema = new Schema({
creationDate: { type: Date, default: Date.now },
comment: { type: String },
});
var conversationSchema = new Schema({
members: [ { type: Schema.Types.ObjectId } ],
comments: [messageSchema]
});
var Member = mongoose.model( 'Member', memberSchema );
var Conversation = mongoose.model( 'Conversation', conversationSchema );
async.waterfall(
[
// Clean
function(callback) {
async.each([Member,Conversation],function(model,callback) {
model.remove({},callback);
},
function(err) {
callback(err);
});
},
// add some people
function(callback) {
async.map(["bill","ted","fred"],function(name,callback) {
Member.create({ "name": name },callback);
},callback);
},
// Create a conversation
function(names,callback) {
var conv = new Conversation();
names.forEach(function(el) {
conv.members.push(el._id);
});
conv.save(function(err,conv) {
callback(err,conv,names)
});
},
// add some comments
function(conv,names,callback) {
async.eachSeries(names,function(name,callback) {
Conversation.update(
{ "_id": conv._id },
{ "$push": { "comments": { "comment": name.name } } },
callback
);
},function(err) {
callback(err,names);
});
},
function(names,callback) {
Conversation.findOne({},function(err,conv) {
callback(err,names,conv.comments[1].creationDate);
});
},
function(names,from,callback) {
var ids = names.map(function(el) {
return el._id
});
var pipeline = [
{ "$match": {
"$and": [
{ "members": ids[0] },
{ "members": ids[1] }
]
}},
{ "$project": {
"members": 1,
"comments": {
"$setDifference": [
{ "$map": {
"input": "$comments",
"as": "c",
"in": { "$cond": [
{ "$gte": [ "$$c.creationDate", from ] },
"$$c",
false
]}
}},
[false]
]
}
}}
];
//console.log(JSON.stringify(pipeline, undefined, 2 ));
Conversation.aggregate(
pipeline,
function(err,result) {
if(err) throw err;
console.log(JSON.stringify(result, undefined, 2 ));
callback(err);
}
)
}
],
function(err) {
if (err) throw err;
process.exit();
}
);
产生以下输出:
[
{
"_id": "55a63133dcbf671918b51a93",
"comments": [
{
"comment": "ted",
"_id": "55a63133dcbf671918b51a95",
"creationDate": "2015-07-15T10:08:51.217Z"
},
{
"comment": "fred",
"_id": "55a63133dcbf671918b51a96",
"creationDate": "2015-07-15T10:08:51.220Z"
}
],
"members": [
"55a63133dcbf671918b51a90",
"55a63133dcbf671918b51a91",
"55a63133dcbf671918b51a92"
]
}
]
请注意,“评论”仅包含最后两个条目,它们与用作输入的日期(大于第二个评论的日期)“大于或等于”。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句