目前,我只有fullname
存储在MongoDB的User
集合中。我想运行一个拆分名字和姓氏的报告,所以现在我正在尝试运行一个聚合,并在发现空白时拆分字符串。
这是我现在所拥有的,但我想根据空白的位置用一个变量来替换硬编码的结束位置。这在聚合管道中可能吗?
db.users.aggregate([{
$project : {
fullname:{ $toUpper:"$fullname" },
first: { $substr: [ "$fullname", 0, 2 ]}, _id:0 }
}, { $sort : { fullname : 1 }
}]);
聚合框架没有任何运算符来基于匹配的字符或任何类似的东西执行"拆分"。当然,只有$substr
需要索引,也没有运算符返回匹配字符的"索引"。
您可以使用mapReduce,它可以使用JavaScript .split()
,但当然,除了主键中的结果之外,mapReduce中没有"排序阶段",这些结果在尝试应用reduce之前总是预先排序的(不会在此处应用所有唯一键):
db.users.mapReduce(
function() {
var lastName = this.fullname.split(/s/).reverse()[0].toUpperCase();
emit({ "lastName": lastName, "orig": this._id },this);
},
function(){}, // Never called on all unique
{ "out": { "inline": 1 } }
);
这将基本上提取空白后的姓氏,将其转换为大写,并将其用作主键中的复合值,这样结果将按该键排序(请注意,不能将_id
用作键名的任何部分,否则将按该字段排序)。
但是,如果你在这里的真实情况是"排序",那么你最好以这种方式存储数据,从而为你提供一个无需计算即可排序的直接值:
var bulk = db.users.initializeOrderedBulkOp(),
count = 0;
db.users.find().forEach(user) {
bulk.find({ "_id": user._id }).updateOne({
"$set": { "lastName": user.fullname.split(/s/).reverse()[0].toUpperCase() }
});
count++;
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.users.initializeOrderedBulkOp();
}
}
if ( count % 1000 != 0 )
bulk.execute();
然后有了一个坚实的字段,你就可以运行你的排序:
db.users.find().sort({ "lastName": 1 });
这将比试图计算一个值来执行排序快得多。
当然,如果排序不是目的,而只是为了表示,那么只需在最有意义的地方在客户端代码中执行拆分即可。聚合框架不能像那样重组数据,虽然mapReduce"可以",但它的输出非常有主见,并不是真正用于此类操作。