我有几个对象需要执行不区分大小写的排序。我被告知我不能将阴影列添加到数据库以规范化值。由于这个原因,我使用聚合来对输出进行排序。
我有一个简单的对象表示如下:
_id:ObjectID
createdOn:创建了时间对象
lastUpdatedOn:上次更新时间对象
name:对象的名称
isActive:表示对象当前是否处于活动状态的布尔值
这是我的聚合函数:
db.organization.aggregate(
[
{
$match: { isActive: true }
},
{
$project: {
lastUpdatedOn: 1,
createdOn: 1,
name: 1,
normalizedName: {$toLower: "$name"}
}
},
{
$sort: { normalizedName: 1 }
},
{
$project: {
lastUpdatedOn: 1,
createdOn: 1,
name: 1
}
}
]
)
它首先确保我们只处理活动对象,为我们的排序创建规范化字段,执行排序,然后删除规范化字段,使其不暴露给用户。它可以根据名称字段按字母顺序放置所有对象,但我不知道是否有更好的方法。
它不一定是一个显着的改进,但根据您的具体用例,投影$$ROOT
以捕获与临时排序列分开的原始文档可以更清晰:
db.organization.aggregate(
[
{
$match: { isActive: true }
},
{
$project: {
doc: '$$ROOT'
normalizedName: {$toLower: "$name"}
}
},
{
$sort: { normalizedName: 1 }
},
{
$project: { doc: 1 }
}
]
)
缺点是你最终在doc
财产中关闭了原始文档,但是很容易通过像Array#map
这样的东西来清理客户端。或者您可以修改最终的$project
以将每个字段提升到顶级,但这可能很乏味。
虽然MongoDB不提供它,但你可以很容易地用PHP或任何其他语言来完成它。以下是如何在PHP中执行此操作。
$cn = new MongoClient($dbHost);
$db = $cn->selectDB($dbName);
$col = new MongoCollection($db, $collectionName);
$cursor = $col->find();
$cursor = iterator_to_array($cursor);
foreach ($cursor as $key => $row) {
$name[$key] = $row['name'];
$email[$key] = $row['email'];
}
//$name is the field to sort on, taken from the above loop
//You can use SORT_ASC or SORT_DESC
array_multisort($name, SORT_ASC, $cursor);
foreach ($cursor as $doc) {
echo $doc['name'].'-'.$doc['email'].'<br/>';
}
我意识到这是一个古老的问题,但由于支持Mongo 3.4 Collation,这使得这更容易。对于Collation with Collation,只需附加一个如下的校对对象:db.organization.aggregate([pipelineobjs],{collation: {locale:'en_US'}})
。
我相信你的具体聚合看起来像:
db.organization.aggregate(
[
{
$match: { isActive: true }
},
{
$project: {
lastUpdatedOn: 1,
createdOn: 1,
name: 1
}
},
{
$sort: { name: 1 }
},
{
$project: {
lastUpdatedOn: 1,
createdOn: 1,
name: 1
}
}
],
{collation: {locale:'en_US'}}
)