我正在查询mongo db中的嵌套Json,示例数据结构如下所示:
{
"_id" : ObjectId("5bf159cc6bf6ab0ac374f80c"),
"name" : "Jack",
"age" : "30",
"info" : {
"0" : {
"status" : "true",
"name" : "luffy"
},
"1" : {
"status" : "true",
"name" : "sanji"
},
"2" : {
"status" : "false",
"name" : "zoro"
}
}
}
/* 2 */
{
"_id" : ObjectId("5bf15f286bf6ab0ac374f8ed"),
"name" : "Mack",
"age" : "33",
"info" : {
"0" : {
"status" : "true",
"name" : "naruto"
},
"1" : {
"status" : "true",
"name" : "sakura"
},
"2" : {
"status" : "false",
"name" : "sasuke"
现在我想做的就是查询并获取status ='true'的那些结果。经过一番谷歌搜索之后,我开始知道如何查询嵌套文档并提出了一个示例。
query:db.getCollection('test').find({"info.0.status":"true"})
但是从上面的查询中可以知道,该查询只会从'0th'数组中获取适当的结果。如何获取查询以遍历数组并返回带有“ status”:“ true”的文档。我也是Mongodb的新手,请忽略任何错误。
注意:其中一位用户告诉我,我应该如下所示重新构建我的数据结构,然后使用$ filter运算符:
[
{
"_id": ObjectId("5bf159cc6bf6ab0ac374f80c"),
"name": "Jack",
"age": "30",
"info": [
{
"status": "true",
"name": "luffy"
},
{
"status": "true",
"name": "sanji"
},
{
"status": "false",
"name": "zoro"
}
]
},
{
"_id": ObjectId("5bf15f286bf6ab0ac374f8ed"),
"name": "Mack",
"age": "33",
"info": [
{
"status": "true",
"name": "naruto"
},
{
"status": "true",
"name": "sakura"
},
{
"status": "false",
"name": "sasuke"
}
]
}
]
但是我没有得到如何按照用户显示的方式来重塑我的结构。还有其他方法可以使用吗?
答案 0 :(得分:1)
一般问题的底线是不要尝试以这种形式“查询”,而是接受建议并重写数据。但是,有不同的记录方法。
正如您已经被告知的那样,最好对集合进行重新建模,使它们成为真实的“数组”,而不是带有命名键的“对象”。
一般情况下,实际上是“迭代”收集项并重写它们:
var updates = [];
db.getCollection('test').find().forEach(doc => {
var info = Object.keys(doc.info).map(k =>
Object.assign({}, doc.info[k], { status: doc.info[k].status === "true" }) );
updates.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": { "$set": { "doc.info": info } }
}
});
if (updates.length >= 1000) {
db.getCollection('test').bulkWrite(updates);
updates = [];
}
});
if (updates.length >= 0) {
db.getCollection('test').bulkWrite(updates);
updates = [];
}
或者编写一个全新的集合:
db.getCollection('test').aggregate([
{ "$addFields": {
"info": {
"$map": {
"input": { "$objectToArray": "$info" },
"in": {
"$mergeObjects": [
"$$this.v",
{ "status": { "$toBool": "$$this.v.status" }
]
}
}
}
}},
{ "$out": "newtest" }
])
基本的依赖就是您是否可以容忍“新集合”,并且实际上拥有MongoDB版本可用的$objectToArray
之类的功能。
即使在“更新”时,通常也建议(尤其是在生产中)改用How to Update Multiple Array Elements in mongodb,因为使用$set
代替整个属性是“蛮力”,而不是安全生产。很好,尽管仅在您自己的系统上进行测试。
完成其中任何一种表格后,您基本上可以$filter
仅进行true
的匹配,如:
collection.aggregate([
// Finds valid "documents"
{ "$match": { "info.status": true } },
// "filters" the array content
{ "$addFields": {
"info": {
"$filter": { "input": "$info", "cond": "$$this.status" }
}
}}
])
您当然可以使用当前文档结构实际进行查询,但这只是 不推荐 :
collection.aggregate([
// Match on transformed object
{ "$match": {
"$expr": {
"$gt": [
{ "$size": {
"$filter": {
"input": {
"$map": {
"input": { "$objectToArray": "$info" },
"in": "$$this.v"
}
},
"cond": { "$toBool": "$$this.status" }
}
}},
0
]
}
}},
// Transform remaining objects
{ "$addFields": {
"info": {
"$filter": {
"input": {
"$map": {
"input": { "$objectToArray": "$info" },
"in": "$$this.v"
}
},
"cond": { "$toBool": "$$this.status" }
}
}
}}
])
或者甚至在$where
中使用JavaScript表达式,当然也不支持在从服务器检索之前实际“过滤”结果内容:
collection.find({
"$where": function() {
return Object.keys(this.info).map( k => this.info[k])
.some(e => e.status === "true")
}
})
使用JavaScript在服务器上更改文档的唯一内容是mapReduce
,它当然具有其自身的特定格式:
collection.mapReduce(
function() {
var id = this._id;
delete this._id;
this.info = Object.keys(this.info)
.map(k => this.info[k])
.filter(o => o.status === "true")
emit(id,this);
},
function() {},
{
"out": { "inline": 1 },
"query": {
"$where": function() {
return Object.keys(this.info).map( k => this.info[k])
.some(e => e.status === "true")
}
}
}
)
在这两种情况下,它们都是“可怕的” ,因为它们基本上依赖于在应用之前可以将每个文档转换为实际的“数组”形式。另一方面,重写集合实际上允许事先完成该工作,因此删除了这样的 computation ,还允许指定"index"以便加快实际查询结果的速度。
简而言之,重写,并且不要“查询”它的当前状态,因为在跨文档查询时,数据库并未真正针对“命名键”进行优化。