Javascript $pull对象来自数组,以及$pull引用来自另一个数组
请从客户机集合中考虑此文档:Javascript $pull对象来自数组,以及$pull引用来自另一个数组,javascript,node.js,mongodb,mongodb-query,Javascript,Node.js,Mongodb,Mongodb Query,请从客户机集合中考虑此文档: client: { services: [ { _id: 111, someField: 'someVal' }, { _id: 222, someField: 'someVal' } ... // More services ] staff: [ { _id: 'aaa', someField: 'someVal', s
client: {
services: [
{
_id: 111,
someField: 'someVal'
},
{
_id: 222,
someField: 'someVal'
}
... // More services
]
staff: [
{
_id: 'aaa',
someField: 'someVal',
servicesProvided: [111, 222, 333, ...]
},
{
_id: 'bbb',
someField: 'someVal',
servicesProvided: [111, 555, 666, ...]
},
{
_id: 'ccc',
someField: 'someVal',
servicesProvided: [111, 888, 999, ...]
}
... // More staff
]
}
一个客户可以有许多员工。每位员工都有他或她提供的服务的参考资料。如果删除了某项服务,则还需要删除所有员工中对该服务的引用
我想从服务
中删除(拉取)一个对象(服务),并在同一查询中删除所有员工
对象中提供的服务
中可能的引用`
例如,如果我使用\u id
111删除服务,我还希望删除提供此服务的staffmembers中对此服务的所有引用
如何编写此查询 这就是事情变得有点糟糕的地方。实际上,如何更新与单个文档中的条件相匹配的“多个”数组项 这里有一点来自操作员的背景信息: 嵌套数组 位置$operator不能用于遍历多个数组的查询,例如遍历嵌套在其他数组中的数组的查询,因为$placeholder的替换项是单个值 这说明了故事的“部分”,但这里针对这个问题的主要观点是“不止一个” 因此,即使“嵌套”部分由于需要执行的操作而没有显式地
true
,但重要的因素是“不止一个”。为了演示,让我们考虑一下:
{
services: [
{
_id: 111,
someField: 'someVal'
},
{
_id: 222,
someField: 'someVal'
}
],
staff: [
{
_id: 'aaa',
someField: 'someVal',
servicesProvided: [111, 222, 333, ...]
},
{
_id: 'bbb',
someField: 'someVal',
servicesProvided: [111, 555, 666, ...]
},
{
_id: 'ccc',
someField: 'someVal',
servicesProvided: [111, 888, 999, ...]
}
]
}
现在,您要求删除111
值。这始终是示例中提供的“第一个”值。因此,我们可以假设情况是这样的,那么更新是“看起来很简单:
db.collection.update(
{
"_id": ObjectId("542ea4991cf4ad425615b84f"),
},
{
"$pull": {
"services": { "_id": 111 },
"staff.servicesProvided": 111
}
}
)
但是。这不会达到您预期的效果,因为元素不会像您预期的那样从所有“staff”数组元素中提取。事实上,它们中没有一个。唯一有效的方法是:
db.collection.update(
{
"_id": ObjectId("542ea4991cf4ad425615b84f"),
"staff.servicesProvided": 111
},
{
"$pull": {
"services": { "_id": 111 },
"staff.$.servicesProvided": 111
}
}
)
但是你猜怎么着!实际上只有“第一个”数组元素被更新了。所以当你看上面的语句时,它基本上是这样说的
再次,假设我们只是在一个现代MongoDB shell中使用MongoDB 2.6版或更高版本的服务器进行测试。那么我们得到的响应如下:
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
请稍等。上一条语句刚刚告诉我们有多少文档被“修改”。因此,尽管我们一次只能更改数组的一个元素,但这里有一些重要的条件反馈
从“Bulk Operations API”操作中获得的新“WriteResult”对象的真正伟大之处在于,它实际上是在shell中完成的,它告诉您是否有东西被上一条语句“修改”了。这比“遗留”要好得多“写下回应,这为我们在循环考虑方面做出一些重要决策提供了基础。例如“我们上次的操作是否确实‘修改’了文档,然后我们是否应该继续?”
因此,这是一个重要的“流控制”点,即使通用MongoDB API本身不能一次“更新所有元素”。现在有一个可测试的案例来决定在循环中“继续”到哪里。这就是我最后所说的“结合”你已经学到的东西的意思。因此,最终我们可以得出如下清单:
var bulk = db.collection.initializeOrderedBulkOp();
var modified = 1;
async.whilst(
function() { return modified },
function(callback) {
bulk.find(
{
"_id": ObjectId("542ea4991cf4ad425615b84f"),
"staff.servicesProvided": 111
}
).updateOne(
{
"$pull": {
"services": { "_id": 111 },
"staff.$.servicesProvided": 111
}
}
);
bulk.execute(function(err,result) {
modified = result.nModfified();
callback(err);
});
},
function(err) {
// did I throw something! Suppose I should so something about it!
}
);
{
"services": [
{
"_id": 111,
"someField": "someVal"
},
{
"_id": 222,
"someField": "someVal"
}
],
"provided": [
{ "_id": "aaa", "service": 111 },
{ "_id": "aaa", "service": 222 },
{ "_id": "aaa", "service": 111 }
]
}
db.collection.update(
{ "_id": ObjectId("542ea4991cf4ad425615b84f") },
{
"$pull": {
"services": { "_id": 111 },
"provided": { "_id": 111 }
}
}
);
或者基本上是那种可爱的东西。因此,您需要从“批量操作”.execute()
中获得的“result”对象来告诉您是否修改了某些内容。在它仍然存在的地方,然后在这里再次“重新迭代”循环,执行相同的更新并再次请求结果
最终,更新操作将告诉您“什么都没有”被修改过。这是当您退出循环并继续正常操作时
现在,处理此问题的另一种方法可能是读入整个对象,然后进行所需的所有修改:
db.collection.findOne(
{
"_id": ObjectId("542ea4991cf4ad425615b84f"),
"staff.servicesProvided": 111
},
function(err,doc) {
doc.services = doc.services.filter(function(item) {
return item._id != 111;
});
doc.staff = doc.staff.filter(function(item) {
item.serviceProvided = item.servicesProvided.filter(function(sub) {
return sub != 111;
});
return item;
});
db.collection.save( doc );
}
);
有点过分了。不完全是原子的,但足够接近测量
因此,您不可能在单个写入操作中真正做到这一点,至少不需要处理“读取”文档,然后在修改内容后“写入”整个内容。但是你可以采取“迭代”的方法,有一些工具可以让你控制它
另一种可能的方法是改变建模方式,如下所示:
var bulk = db.collection.initializeOrderedBulkOp();
var modified = 1;
async.whilst(
function() { return modified },
function(callback) {
bulk.find(
{
"_id": ObjectId("542ea4991cf4ad425615b84f"),
"staff.servicesProvided": 111
}
).updateOne(
{
"$pull": {
"services": { "_id": 111 },
"staff.$.servicesProvided": 111
}
}
);
bulk.execute(function(err,result) {
modified = result.nModfified();
callback(err);
});
},
function(err) {
// did I throw something! Suppose I should so something about it!
}
);
{
"services": [
{
"_id": 111,
"someField": "someVal"
},
{
"_id": 222,
"someField": "someVal"
}
],
"provided": [
{ "_id": "aaa", "service": 111 },
{ "_id": "aaa", "service": 222 },
{ "_id": "aaa", "service": 111 }
]
}
db.collection.update(
{ "_id": ObjectId("542ea4991cf4ad425615b84f") },
{
"$pull": {
"services": { "_id": 111 },
"provided": { "_id": 111 }
}
}
);
等等。因此,查询变成如下所示:
var bulk = db.collection.initializeOrderedBulkOp();
var modified = 1;
async.whilst(
function() { return modified },
function(callback) {
bulk.find(
{
"_id": ObjectId("542ea4991cf4ad425615b84f"),
"staff.servicesProvided": 111
}
).updateOne(
{
"$pull": {
"services": { "_id": 111 },
"staff.$.servicesProvided": 111
}
}
);
bulk.execute(function(err,result) {
modified = result.nModfified();
callback(err);
});
},
function(err) {
// did I throw something! Suppose I should so something about it!
}
);
{
"services": [
{
"_id": 111,
"someField": "someVal"
},
{
"_id": 222,
"someField": "someVal"
}
],
"provided": [
{ "_id": "aaa", "service": 111 },
{ "_id": "aaa", "service": 222 },
{ "_id": "aaa", "service": 111 }
]
}
db.collection.update(
{ "_id": ObjectId("542ea4991cf4ad425615b84f") },
{
"$pull": {
"services": { "_id": 111 },
"provided": { "_id": 111 }
}
}
);
这确实是一个单一的更新操作,一次删除所有内容,因为每个元素都包含在单一数组中
所以有很多方法可以做到这一点,但建模方式实际上取决于应用程序数据访问模式。选择最适合您的解决方案。这就是您首先选择MongoDB的原因。现在对您的其他链接发表了评论,但我想我理解您的意图。通过显示共享相同“服务”id值的“多个”“人员”条目可能更好地演示。但这就是你要问的,对吗?如何从“staff”数组的“多个”成员下的子数组中删除该条目。对吗?顺便说一句,如果我没记错的话,你在node.js和mongoskin上。是的!这就是我想要的=)。Nodejs和mongoskin正确!酷。是的,这是“可能的”,但不是在单个查询中。我想是时候把前面问题的一些部分放在一起了:)如果这是一个复杂的操作,我应该把它看作是数据结构不好的标志吗?本周早些时候,我将员工和服务分为不同的集合,通过参考链接到一个客户。我后来将它们嵌入到客户机中,因为我推断它们总是在特定客户机的上下文中被“看到”,并且它们可能也不会变得很大。我刚刚意识到,如果我要在多个查询中执行此操作,这一点都不难!步骤1:从DB获取客户机,并仅投影staff数组。步骤2:删除内存中json对象上给定服务id的所有实例。步