MongoDb:如何从文档中获取字段(子文档)
MongoDb: How to get a field (sub document) from a document?
考虑以下示例集合:
{
"_id:"0,
"firstname":"Tom",
"children" : {
"childA":{
"toys":{
'toy 1':'batman',
'toy 2':'car',
'toy 3':'train',
}
"movies": {
'movie 1': "Ironman"
'movie 2': "Deathwish"
}
},
"childB":{
"toys":{
'toy 1':'doll',
'toy 2':'bike',
'toy 3':'xbox',
}
"movies": {
'movie 1': "Frozen"
'movie 2': "Barbie"
}
}
}
}
现在我只想从特定文档中检索电影。
我试过这样的东西:
movies = users.find_one({'_id': 0}, {'_id': 0, 'children.ChildA.movies': 1})
然而,我得到了从"儿童"到"电影"的整个领域结构,它是内容。如何只进行查询并只检索"电影"的内容?
具体来说,我想以这个结束:
{
'movie 1': "Frozen"
'movie 2': "Barbie"
}
这里的问题是您当前的数据结构不太适合查询。这主要是因为您使用"键"来实际表示"数据点",虽然最初看起来这是一个合乎逻辑的想法,但实际上这是一种非常糟糕的做法。
因此,与其将"childA"answers"childB"分配为对象或"子文档"的键,不如将这些"值"分配给这样一个结构中的通用键名:
{
"_id:"0,
"firstname":"Tom",
"children" : [
{
"name": "childA",
"toys": [
"batman",
"car",
"train"
],
"movies": [
"Ironman"
"Deathwish"
]
},
{
"name": "childB",
"toys": [
"doll",
"bike",
"xbox",
],
"movies": [
"Frozen",
"Barbie"
]
}
]
}
不是最好的,因为有嵌套数组,这可能是一个潜在的问题,但也有解决方法(但稍后),但这里的要点是,这比在"键"中定义数据要好得多。命名不一致的"密钥"的主要问题是,MongoDB通常不允许以任何方式"通配符"这些名称,因此您只能使用命名和"绝对路径"来访问元素,如:
儿童->儿童A->玩具
儿童->儿童B->玩具
简而言之,这是糟糕,与此相比:
"children.toys"
从上面准备的样本来看,我想说这是一种更好的组织数据的方法。
即便如此,对于MongoDB中的标准.find()
类型查询来说,仅仅返回诸如"电影的唯一列表"之类的内容是不可能的。这实际上需要更多的"文档操作",并且在MongoDB的聚合框架中得到了很好的支持。这具有查询方法中没有的广泛操作功能,作为具有上述结构的每个文档的响应,您可以这样做:
db.collection.aggregate([
# De-normalize the array content first
{ "$unwind": "$children" },
# De-normalize the content from the inner array as well
{ "$unwind": "$children.movies" },
# Group back, well optionally, but just the "movies" per document
{ "$group": {
"_id": "$_id",
"movies": { "$addToSet": "$children.movies" }
}}
])
因此,现在文档中的"列表"响应只包含"唯一"电影,这更符合您的要求。或者,您也可以$push
并创建一个"非唯一"列表。但愚蠢的是,这实际上与此相同:
db.collection.find({},{ "_id": False, "children.movies": True })
作为一个"集合范围"的概念,您可以通过简单地使用.distinct()
方法来简化它。它基本上形成了一个基于您提供的输入的"不同"键列表。这在阵列上玩得很好:
db.collection.distinct("children.toys")
这本质上是对集合中每个"玩具"值的所有"不同"事件的集合范围分析,并作为一个简单的"数组"返回。
但就你现有的结构而言,它值得一个解决方案来解释,但你真的必须明白,这种解释是可怕的。这里的问题是,通用查询和聚合方法可用的"本机"和优化方法根本不可用,唯一可用的选项是基于JavaScript的处理。即使通过"v8"引擎集成稍微好一点,但与本机代码方法相比,它仍然是一个完全的懒散。
因此,从你所拥有的"原始"形式来看,(JavaScript形式,函数必须非常容易翻译"):
db.collection.mapReduce(
// Mapper
function() {
var id this._id;
children = this.children;
Object.keys(children).forEach(function(child) {
Object.keys(child).forEach(function(childKey) {
Object.keys(childKey).forEach(function(toy) {
emit(
id, { "toys": [children[childkey]["toys"][toy]] }
);
});
});
});
},
// Reducer
function(key,values) {
var output = { "toys": [] };
values.forEach(function(value) {
value.toys.forEach(function(toy) {
if ( ouput.toys.indexOf( toy ) == -1 )
output.toys.push( toy );
});
});
},
{
"out": { "inline": 1 }
}
)
因此,JavaScript评估是"可怕"的方法,因为这在执行中要慢得多,并且您可以看到需要实现的"遍历"代码。这对性能来说是个坏消息,所以不要这么做。改为改变结构。
作为最后一部分,您可以对其进行不同的建模,以避免"嵌套数组"的概念。要明白,"嵌套数组"的真正问题是,如果不阅读整个文档并对其进行修改,"更新"嵌套元素是不可能的
因此$push
和$pull
方法工作良好。但是使用"位置"$
运算符是不起作用的,因为"外部"数组索引总是"第一个"匹配元素。因此,如果这真的是一个问题,那么你可以做这样的事情,例如:
{
"_id:"0,
"firstname":"Tom",
"childtoys" : [
{
"name": "childA",
"toy": "batman"
}.
{
"name": "childA",
"toy": "car"
},
{
"name": "childA",
"toy": "train"
},
{
"name": "childB",
"toy": "doll"
},
{
"name": "childB",
"toy": "bike"
},
{
"name": "childB",
"toy": "xbox"
}
],
"childMovies": [
{
"name": "childA"
"movie": "Ironman"
},
{
"name": "childA",
"movie": "Deathwish"
},
{
"name": "childB",
"movie": "Frozen"
},
{
"name": "childB",
"movie": "Barbie"
}
]
}
如果您确实需要定期"更新"项目,而不仅仅是$push和$pull项目到"玩具"answers"电影"数组,那么这将是避免嵌套更新问题的一种方法。
但这里的总体信息是围绕您实际使用的访问模式来设计数据。MongoDB通常不喜欢有"严格路径"的东西,因为它可以查询或以其他方式灵活发布更新。
MongoDB中的投影使用"1"answers"0",而不是"True"/"False"。此外,确保字段以正确的大小写(大写/小写)指定
查询应如下所示:
db.users.findOne({'_id': 0}, {'_id': 0, 'children.childA.movies': 1})
这将导致:
{
"children" : {
"childA" : {
"movies" : {
"movie 1" : "Ironman",
"movie 2" : "Deathwish"
}
}
}
}
- 如何在新的Meteor Collection文档中设置服务器上的created_on字段
- 循环访问文档 (validate_doc_update) 中的 CouchDB 字段
- findAndModify,如何对文档进行操作's数组搜索对象并更改字段值
- 将文档的MongoDB字段合并到一个文档中
- 如何在一个字段中只提取6个值为true的文档
- 可编辑文档中多个字段的本地存储
- 查找包含子对象MongoDb和Node.js的特定字段的文档
- Mongoose创建一个文档,如果找不到指定的字段,则更新文档中的数组
- 仅返回嵌入文档列表中的某些字段
- 在MongoDB和Node.js中查找某个字段具有与对象完全相同元素的文档
- 将多个文档中的字段保存到对象
- 在响应之前向文档添加其他字段
- 如何从MongoDB中选择所有文档,其中一个字段不等于某个值
- MongoDB:按日历周和年聚合文档,并计算一个字段的所有值的总量
- 在MongoDB中,从文档中获取单个字段数组的最简单方法是什么
- 来自 JavaScript 网站的推文输入字段
- CouchDB:访问_design视图或提取文档字段的替代方法是什么
- 为couchdb更新nodejs的nano模块中的文档字段
- 如何$inc MongoDB文档字段与AJAX
- Mongodb /meteor收集检查子文档字段是否存在,当字段是一个变量