在mongoDb中,如何通过索引删除数组元素

时间:2011-01-03 20:58:01

标签: mongodb

{

        "_id" : ObjectId("4d1cb5de451600000000497a"),           
        "name" : "dannie",  
        "interests" : [  
            "guitar",  
            "programming",           
            "gadgets",  
            "reading"  
        ]   
}

在上面的示例中,假设上述文档位于 db.people 集合中。如何通过 索引 删除兴趣数组的第3个元素?

编辑:

这是我目前的解决方案:

var interests = db.people.findOne({"name":"dannie"}).interests;  
interests.splice(2,1)  
db.people.update({"name":"dannie"}, {"$set" : {"interests" : interests}});

有更直接的方法吗?

10 个答案:

答案 0 :(得分:128)

没有直接的数组索引拉/删的方法。事实上,这是一个悬而未决的问题http://jira.mongodb.org/browse/SERVER-1014,您可以投票支持它。

解决方法是使用$ unset然后$ pull:

db.lists.update({}, {$unset : {"interests.3" : 1 }}) 
db.lists.update({}, {$pull : {"interests" : null}})

更新:如某些评论中所述,此方法不是原子的,如果其他客户端在两个操作之间读取和/或写入,则会导致某些竞争条件。如果我们需要将操作变为原子操作,我们可以:

  • 从数据库中读取文档
  • 更新文档并删除数组中的项目
  • 替换数据库中的文档。为了确保自我们阅读以来文档没有更改,我们可以使用更新,如果当前模式描述in the mongo docs

答案 1 :(得分:18)

您可以使用$pull update操作修饰符来删除数组中的特定元素。如果您提供的查询将如下所示:

db.people.update({"name":"dannie"}, {'$pull': {"interests": "guitar"}})

此外,您可以考虑使用$pullAll删除所有匹配项。有关详细信息,请参阅官方文档页面 - http://www.mongodb.org/display/DOCS/Updating#Updating-%24pull

这不会使用索引作为删除元素的条件,但在类似于您的情况下仍然可能会有所帮助。 IMO,使用索引来寻址数组中的元素并不是非常可靠,因为mongodb在元素顺序上并不像我所知的那样一致。

答案 2 :(得分:4)

在Mongodb 4.2中,您可以执行以下操作:

db.example.update({}, [
     {$set: {field: {
           $concatArrays: [ 
                  {$slice: ["$field", P]}, 
                  {$slice: ["$field", {$add: [1, P]}, {$size: "$field"}]}
           ]
     }}}
]);

P是要从数组中删除的元素的索引。

如果您要从P中删除,直到结束:

db.example.update({}, [
     {$set: {field: {$slice: ["$field", P]}}
]);

答案 3 :(得分:3)

我不是使用unset(如在接受的答案中),而是通过将字段设置为唯一值(即非NULL)然后立即拉出该值来解决此问题。从异步角度来看更安全一些。这是代码:

    var update = {};
    var key = "ToBePulled_"+ new Date().toString();
    update['feedback.'+index] = key;
    Venues.update(venueId, {$set: update});
    return Venues.update(venueId, {$pull: {feedback: key}});

希望mongo能解决这个问题,或许可以通过扩展$ position修饰符来支持$ pull和$ push。

答案 4 :(得分:2)

我建议使用GUID(我倾向于使用ObjectID)字段,或者为数组中的每个子文档使用自动递增字段。

使用此GUID,很容易发出$ pull,并确保将拉出正确的。其他阵列操作也是如此。

答案 5 :(得分:2)

Mongo 4.4开始,$function聚合运算符允许应用自定义javascript函数来实现MongoDB查询语言不支持的行为。

例如,为了通过删除给定索引处的元素来更新数组:

// { "name": "dannie", "interests": ["guitar", "programming", "gadgets", "reading"] }
db.collection.update(
  { "name": "dannie" },
  [{ $set:
    { "interests":
      { $function: {
          body: function(interests) { interests.splice(2, 1); return interests; },
          args: ["$interests"],
          lang: "js"
      }}
    }
  }]
)
// { "name": "dannie", "interests": ["guitar", "programming", "reading"] }

$function具有3个参数:

  • body,这是要应用的函数,其参数是要修改的数组。这里的功能仅在于使用拼接删除索引2处的1个元素。
  • args,其中包含body函数作为参数的记录中的字段。在我们的情况下,"$interests"
  • lang,这是编写body函数的语言。当前仅js可用。

答案 6 :(得分:0)

适用于使用带有nodejs的猫鼬搜索答案的人。这就是我的方法。

exports.deletePregunta = function (req, res) {
let codTest = req.params.tCodigo;
let indexPregunta = req.body.pregunta; // the index that come from frontend
let inPregunta = `tPreguntas.0.pregunta.${indexPregunta}`; // my field in my db
let inOpciones = `tPreguntas.0.opciones.${indexPregunta}`; // my other field in my db
let inTipo = `tPreguntas.0.tipo.${indexPregunta}`; // my  other field in my db

Test.findOneAndUpdate({ tCodigo: codTest },
    {
        '$unset': {
            [inPregunta]: 1, // put the field with [] 
            [inOpciones]: 1,
            [inTipo]: 1
        }
    }).then(()=>{ 
    Test.findOneAndUpdate({ tCodigo: codTest }, {
        '$pull': {
            'tPreguntas.0.pregunta': null,
            'tPreguntas.0.opciones': null,
            'tPreguntas.0.tipo': null
        }
    }).then(testModificado => {
        if (!testModificado) {
            res.status(404).send({ accion: 'deletePregunta', message: 'No se ha podido borrar esa pregunta ' });
        } else {
            res.status(200).send({ accion: 'deletePregunta', message: 'Pregunta borrada correctamente' });
        }
    })}).catch(err => { res.status(500).send({ accion: 'deletePregunta', message: 'error en la base de datos ' + err }); });
 }

如果答案不是很清楚,我可以重写此答案,但是我认为可以。

希望这对您有所帮助,我在此问题上浪费了很多时间。

答案 7 :(得分:0)

我的建议是除非绝对必要,否则不要这样做。我在这个线程中实现了一个解决方案,发现它在删除哪个元素方面产生了意外行为。 我的解决方案(由于时间限制)只是在每次将某些内容推送到数据库时将唯一 id 推送到数组。

答案 8 :(得分:0)

有点晚了,但有些人可能会发现它对使用 robo3t 的人有用-

db.getCollection('people').update( 
  {"name":"dannie"}, 
  { $pull: 
   {
       interests: "guitar" // you can change value to 
   } 
  }, 
  { multi: true }
);

如果你有类似 -

property: [ 
    {
        "key" : "key1",
        "value" : "value 1"
    }, 
    {
        "key" : "key2",
        "value" : "value 2"
    }, 
    {
        "key" : "key3",
        "value" : "value 3"
    }
]

并且您想删除键为 key3 的记录,那么您可以使用一些东西 -

 db.getCollection('people').update( 
  {"name":"dannie"}, 
  { $pull: 
   {
       property: { key: "key3"} // you can change value to 
   } 
  }, 
  { multi: true }
);

嵌套属性也是如此。

答案 9 :(得分:-2)

我们可以使用$ pop通过索引删除数组中的元素,而不是使用$ pull。但是你应该从索引位置减去1,以便根据索引进行删除。

对于E.g,如果要删除索引0中的元素,则应使用-1,对于索引1,应使用0,依此类推......

查询删除第3个元素(小工具):

void *

供参考:https://docs.mongodb.com/manual/reference/operator/update/pop/