假设我们从MongoDB的$group文档中收集了以下书籍:
{ "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 }
{ "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 }
{ "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 }
{ "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 }
{ "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }如果我们效仿他们,按作者分组,如下所示:
db.books.aggregate(
[
{ $group : { _id : "$author", books: { $push: "$title" } } }
]
)然后我们得到一个数组:
[
{ "_id" : "Homer", "books" : [ "The Odyssey", "Iliad" ] },
{ "_id" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }
]但是我更喜欢使用字典而不是数组。
{
"Homer": { "books" : [ "The Odyssey", "Iliad" ] },
"Dante": { "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }
}换句话说,我想在字典中使用_id作为键。这将使接收方更容易访问,因为当他们想要查找特定的作者时,不需要在数组中搜索。
显然,当接收者得到数据时,他们可以重新安排数据。但是,有什么办法可以通过Mongo的聚合管道来实现呢?
(对于额外的分数,当_id具有多个属性时,输出嵌套字典,例如,每个发布者有一个键,然后在发布者下面为每个作者提供一个键。)
发布于 2016-11-15 11:56:21
如果您需要比聚合框架所允许的更多的灵活性,您可以尝试使用map- than。
map = function() {
var books = {};
books[this._id] = this.title;
emit(this.author, books);
}
reduce = function(key, values) {
var result = {};
values.forEach(function(value) {
for (var id in value) {
result[id] = value[id];
}
});
return result;
}发布于 2016-11-16 02:38:18
我可能会在某个时候尝试一下地图-减少方法。
现在,我收到数据时正在用Javascript处理数据:
/**
* Flattens an array of items with _ids into an object, using the _ids as keys.
*
* For example, given an array of the form:
*
* [
* { _id: 'X', foo: 'bar' },
* { _id: 'Y', foo: 'baz' }
* ]
*
* Will produce an object ("dictionary") of the form:
*
* {
* X: { foo: 'bar' },
* Y: { foo: 'baz' }
* }
*
* Note that the `_id` properties will be removed from the input array!
*/
function flattenBy_id (array) {
const obj = {};
array.forEach(item => {
const id = item._id;
if (typeof id !== 'string' && typeof id !== 'number' && typeof id !== 'boolean') {
throw Error(`Cannot flatten: _id is non-primitive (${typeof id}) in item: ${JSON.stringify(item)}`);
}
delete item._id;
obj[id] = item;
});
return obj;
}可以在一行中使用LoDash产生类似的结果。
_.keyBy(array, '_id')但是,这不会删除_id属性,对于我的目的来说,这是更干净的。
下面是当_id具有多个属性时创建嵌套对象的版本:
/**
* Flattens an array of items with _ids into an object, using the _ids as keys.
*
* For example, given an array of the form:
*
* [
* { _id: {publisher: 'P', author: 'Q', book: 'Alice in Wonderland'}, date: 1940, content: '...' },
* { _id: {publisher: 'X', author: 'Y', book: 'The Hobbit'}, date: 1950, content: '...' },
* { _id: {publisher: 'X', author: 'Y', book: 'The Lord of the Rings'}, date: 1960, content: '...' },
* { _id: {publisher: 'X', author: 'Z', book: 'Harry Potter'}, date: 1990, content: '...' },
* ]
*
* Will produce an object ("dictionary") of the form:
*
* {
* P: {
* Q: {
* 'Alice in Wonderland': {date: 1940, content: '...'}
* }
* },
* X: {
* Y: {
* 'The Hobbit': {date: 1950, content: '...'},
* 'The Lord of the Rings': {date: 1960, content: '...'}
* },
* Z: {
* 'Harry Potter': {date: 1990, content: '...'}
* }
* }
* }
*
* Note that the `_id` properties will be removed from the input array!
*/
function flattenBy_id (array) {
const dictionary = {};
array.forEach(item => {
const path = item._id;
const pathArray = typeof path === 'object' ? Object_values(path) : [path];
let target = dictionary;
pathArray.forEach((key, i) => {
// Check that key is a primitive
// Not throwing on 'undefined' because we sometimes have (very old) data with that key
if (typeof key !== 'string' && typeof key !== 'number' && typeof key !== 'boolean') {
throw Error(`Cannot flatten: _id is non-primitive (${typeof key}) in item: ${safeStringify(item)}`);
}
// Are we on the final step of the path, or before it?
if (i < pathArray.length - 1) {
// We are not at the end of the path. Travel one step.
if (target[key] === undefined) {
target[key] = {};
}
target = target[key];
} else {
// We are on the final step of the path
// We don't want to overwrite data that already exists. We should never be given input of that form.
if (target[key] !== undefined) {
throw Error(`Cannot flatten: The key "${key}" already appears in ${safeStringify(target)} while trying to add: ${safeStringify(item._id)}`);
}
delete item._id;
target[key] = item;
}
});
});
return dictionary;
}它假定_id属性总是以相同的顺序排列。希望这是蒙戈的$group运营商的一贯行为。
如果_id不总是包含相同数量的属性,则它将无法正常工作,并可能引发错误。(例如,_id: {foo: 1, bar: 2}后面跟着_id: {foo: 1}会带来麻烦。如果某些文档未定义bar,则可能发生这种情况。)如果您有这种类型的数据,则需要一种不同的方法。
https://stackoverflow.com/questions/40608174
复制相似问题