我有一个MongoDB文档,其记录如下:
[
{ _id: id, category_name: 'category1', parent_category: null },
{ _id: id, category_name: 'category2', parent_category: null },
{ _id: id, category_name: 'subcategory1', parent_category: id_parent_1 },
{ _id: id, category_name: 'subcategory2', parent_category: id_parent_1 },
{ _id: id, category_name: 'subcategory3', parent_category: id_parent_2 },
{ _id: id, category_name: 'subcategory4', parent_category: id_parent_2 }
]
如您所见,我正在存储parent_category
null
的类别,子类别包含父类别的ID。我正在寻找的是将这些分组为这样的格式:
[
{ category_name: 'category1', categories: [
{ category_name: 'subcategory1', _id: id },
{ category_name: 'subcategory2', _id: id }
]
},
{ category_name: 'category2', categories: [
{ category_name: 'subcategory3', _id: id },
{ category_name: 'subcategory4', _id: id }
]
}
]
因此,基本上将父类别与具有子类别的数组进行分组。我正在使用猫鼬。我尝试使用MongoDB提供的聚合框架,但我无法获得所需的结果。 :(
我有权以任何可能需要的方式修改架构!
提前致谢!
答案 0 :(得分:2)
您似乎将Mongo视为关系数据库(将所有这些字段分开并将它们与查询结合在一起)。你应该做的是重建你的架构。例如:
var CategorySchema = new Schema({
category_name: String,
subCategories:[subCategorySchema]
}
var subCategorySchema = new Schema({
category_name: String
})
这种方式当你需要查询集合时,这是一个简单的
db.find({category_name: "name of the category"}, function(){})
获得您需要的一切。
以防万一:您可以使用简单的更新将子类别添加到数组中。请阅读this了解详情。
答案 1 :(得分:1)
如果您的架构未更改,请尝试此操作:
var MongoClient = require('mongodb').MongoClient
//connect away
MongoClient.connect('mongodb://127.0.0.1:27017/test', function(err, db) {
if (err) throw err;
console.log("Connected to Database");
//simple json record
var document = [];
//insert record
//db.data.find({"parent_category":null }).forEach(function(data) {print("user: " + db.data.findOne({"parent_category":data._id })) })
db.collection('data').find({"parent_category":null }, function(err, parentrecords) {
if (err) throw err;
var cat ={};
parentrecords.forEach(function(data){
cat["category_name"] = data["category_name"];
db.collection('data').find({"parent_category":data._id },function(err, childrecords) {
var doc = [];
childrecords.forEach(function(childdata){
doc.push(childdata);
},function(){
cat["categories"] = doc;
document.push(cat);
console.log(document);
});
});
});
});
});
答案 2 :(得分:1)
如果您想在不更改架构的情况下找到预期结果,那么您基本上会遵循一些复杂的mongo aggregation
查询。要查找输出,请按以下步骤操作:
$project
检查parent_category
等于null
如果为真,则添加_id
否则添加parent_category
。key name as parent_id
礼物和parent_id
分组相似,并推送剩余数据,如category_name and parent_category
。unwind
只有单个数组对象,所以这个单个数组对象和用于显示那些要显示的字段的项目。检查工作聚合查询,如下所示:
db.collectionName.aggregate({
"$project": {
"parent_id": {
"$cond": {
"if": {
"$eq": ["$parent_category", null]
},
"then": "$_id",
"else": "$parent_category"
}
},
"category_name": 1,
"parent_category": 1
}
}, {
"$group": {
"_id": "$parent_id",
"categories": {
"$push": {
"category_name": "$category_name",
"parent_category": "$parent_category"
}
},
"parentData": {
"$push": {
"$cond": {
"if": {
"$eq": ["$parent_category", null]
},
"then": {
"category_name": "$category_name",
"parent_category": "$parent_category"
},
"else": null
}
}
}
}
}, {
"$project": {
"findParent": {
"$setIntersection": ["$categories", "$parentData"]
},
"categories": {
"$setDifference": ["$categories", "$parentData"]
}
}
}, {
"$unwind": "$findParent"
}, {
"$project": {
"category_name": "$findParent.category_name",
"categories": 1
}
}).pretty()
答案 3 :(得分:-1)
要按字段对记录进行分组,请尝试在聚合中使用 $ group 。这对我有用。
示例强>
SYNOPSIS
globex PATTERNLIST[, \%options]
DESCRIPTION
Extends the standard glob() function with support for recursive globbing.
Prepend '**/' to the part of the pattern that should match anywhere in the
subtree or end the pattern with '**' to match all files and dirs. in the
subtree, similar to Bash's `globstar` option.
A pattern that doesn't contain '**' is passed to the regular glob()
function.
While you can use brace expressions such as {a,b}, using '**' INSIDE
such an expression is NOT supported, and will be treated as just '*'.
Unlike with glob(), whitespace in a pattern is considered part of that
pattern; use separate pattern arguments or a brace expression to specify
multiple patterns.
To also follow directory symlinks, set 'follow' to 1 in the options hash
passed as the optional last argument.
Note that this changes the sort order - see below.
Traversal:
For recursive patterns, any given directory examined will have its matches
listed first, before descending depth-first into the subdirectories.
Hidden directories:
These are skipped by default, onless you set 'hiddendirs' to 1 in the
options hash passed as the optional last argument.
Sorting:
A given directory's matching items will always be sorted
case-insensitively, as with glob(), but sorting across directories
is only ensured, if the option to follow symlinks is NOT specified.
Duplicates:
Following symlinks only prevents cycles, so if a symlink and its target
they will both be reported.
(Under the hood, following symlinks activates the following
File::Find:find() options: `follow_fast`, with `follow_skip` set to 2.)
Since the default glob() function is at the heart of this function, its
rules - and quirks - apply here too:
- If literal components of your patterns contain pattern metacharacters,
- * ? { } [ ] - you must make sure that they're \-escaped to be treated
as literals; here's an expression that works on both Unix and Windows
systems: s/[][{}\-~*?]/\\$&/gr
- Unlike with glob(), however, whitespace in a pattern is considered part
of the pattern; to specify multiple patterns, use either a brace
expression (e.g., '{*.txt,*.md}'), or pass each pattern as a separate
argument.
- A pattern ending in '/' restricts matches to directories and symlinks
to directories, but, strangely, also includes symlinks to *files*.
- Hidden files and directories are NOT matched by default; use a separate
pattern starting with '.' to include them; e.g., globex '**/{.*,*}'
matches all files and directories, including hidden ones, in the
current dir.'s subtree.
Note: As with glob(), .* also matches '.' and '..'
- Tilde expansion is supported; escape as '\~' to treat a tilde as the
first char. as a literal.
- A literal path (with no pattern chars. at all) is echoed as-is,
even if it doesn't refer to an existing filesystem item.
COMPATIBILITY NOTES
Requires Perl v5.6.0+
'/' must be used as the path separator on all platforms, even on Windows.
EXAMPLES
# Find all *.txt files in the subtree of a dir stored in $mydir, including
# in hidden subdirs.
globex "$mydir/*.txt", { hiddendirs => 1 };
# Find all *.txt and *.bak files in the current subtree.
globex '**/*.txt', '**/*.bak';
# Ditto, though with different output ordering:
# Unlike above, where you get all *.txt files across all subdirs. first,
# then all *.bak files, here you'll get *.txt files, then *.bak files
# per subdirectory encountered.
globex '**/{*.txt,*.bak}';
# Find all *.pm files anywhere in the subtrees of the directories in the
# module search path, @INC; follow symlinks.
# Note: The assumption is that no directory in @INC has embedded spaces
# or contains pattern metacharacters.
globex '{' . (join ',', @INC) . '}/**/*.pm', { follow => 1 };
<强>参考强>: MongoDB Aggregation
希望这有效。