MongoDB插入数据时出现重复键错误。

18

我在试图向一个的mongodb集合进行批量插入时,遇到了以下错误:

pymongo.errors.DuplicateKeyError: E11000重复键错误索引: cmdDistros.locDistro.$id dup key: { : ObjectId('51dac9d0c74cd81acd85c0fd') }

我没有在创建任何文档时指定_id,因此MongoDB应该正确地创建唯一索引。这是我使用的代码:

#Populate database with uniform distribution
            entries = []
            for coor in freeIndices:
                for theta in range(360):
                    entry = {"x" : coor[0], "y" : coor[1], "heading" : theta}
                    for i in range(numData):
                            entry["data" + str(i)] = 1./numData
                    entries.append(entry)
            print "Entries created, loading into database..."

            locDistro.insert(entries)

将命运掌握在自己手中,我尝试使用以下方法创建自己的索引:

#Populate database with uniform distribution
            entries = []
            idNum = 0
            for coor in freeIndices:
                for theta in range(360):
                    print idNum
                    entry = {"_id" : idNum, "x" : coor[0], "y" : coor[1], "heading" : theta}
                    idNum += 1
                    for i in range(numData):
                            entry["data" + str(i)] = 1./numData
                    entries.append(entry)
            print "Entries created, loading into database..."

            locDistro.insert(entries, manipulate = False)

在创建文档时,打印语句显示了每个idnum,它们都是唯一的并按预期递增。然而,在插入文档时,我收到了错误信息:

pymongo.errors.DuplicateKeyError: E11000 duplicate key error index: cmdDistros.locDistro.$id dup key: { : 0 }

只有一个文档被插入到我的数据库中。

我完全被难住了,有人知道为什么会发生这种情况吗?


1
我不知道发生了什么,但问题似乎已经自行解决了...我只是一遍又一遍地运行代码,然后它就奇迹般地工作了...很奇怪...如果有人能解释一下,我仍然想知道,以防再次发生...顺便说一下,entries.append这一行是打错了,实际位置应该与上面的for对齐。 - RoboCop87
集合中是否定义了其他索引? - WiredPrairie
仅_id被索引,我想使用ensure_index在x、y和heading上创建一个索引,但它们不是唯一的,所以我不确定是否会起作用。无论如何,目前仅_id被索引。 - RoboCop87
现在这两个选项都能用了吗?一个是由驱动程序创建的 _id,另一个是由您自己创建的 _id。 - innoSPG
现在驱动程序创建的_id正在工作,我还没有重试创建自己的_id,经验告诉我,在确认代码可行之后,我不愿意轻易更改它。 - RoboCop87
你是如何创建 locDistro 对象的?顺便说一下,你应该能够通过单击它来取消勾选接受标志。 - Xavier Combelle
5个回答

29

你需要明白的是,你的入口列表引用了同一个条目字典。因此,当PyMongo设置entries[0]['_id']时,所有其他的条目都会得到相同的_id。(实际上,PyMongo将遍历整个列表来设置每个条目的_id,所以最终所有的条目将具有相同的_id)。一个快速的解决方法是:

entries.append(entry.copy())

这只是一个浅复制,但在你分享的代码中,我相信这已足以解决你的问题。


2
每次循环都创建一个条目,它们如何指向同一个对象?我认为这不是正确的答案。 - Arshad Ansari
我也遇到了pymongo的“insert_one”方法的奇怪行为。我听说过使用“copy”方法和“del obj['_id']”方法。不过,我还是不太明白“copy”方法。即使对象不同,我每次插入一个对象时都要进行复制吗? - addicted
当使用相同引用lol插入多个对象时,此答案也适用于NodeJS。谢谢。 - Ricky Boyce
这解决了我的问题,但我仍不清楚为什么会这样。entry 在每次迭代之前都被初始化,然后附加到 entries 中。似乎有一些奇怪的事情发生了。 - Anthony Awuley

10

5
我曾经使用 insert_one()insert_many() 时遇到了同样的错误。
我的解决方案是,使用 update_one() 并添加参数 upsert=True
  doc = {a: 1, b:2, x:{xx:"hello",yy:"world"}}
  db.collection.update_one(doc,{'$set':doc},upsert=True)

这对我来说可行 :-)


这个很不错。对我有用。 - igorkf
这似乎无法解决我的问题。 - Shashwat Swain

3

确保每次插入后都清除变量“entries”。

问题在于,如果文档中不存在_id字段,则PyMongo会在插入之前将其注入到文档中(_id始终由客户端生成)。这意味着第一次通过循环时,_id是通过插入方法添加的。由于'entries'是在外部定义的,因此每次通过循环时都使用相同的_id值。

在循环语句顶部清除字典变量。

或者

从字典中删除_id。例如:

del my_dict['_id'] 

1
解决方案:在循环内部声明dict()项,然后填充并插入它。 我在使用pymongo的insert_one()时遇到了类似的问题。我通过在循环内部声明dict()项来解决了我的问题。 以下是您代码的可工作版本:
#Populate database with uniform distribution
            entries = []
            for coor in freeIndices:
                for theta in range(360):
                    entry = dict()
                    entry['x'] = coor[0]
                    entry['y'] = coor[1]
                    entry['heading'] = theta
             
                    for i in range(numData):
                            entry['data' + str(i)] = 1./numData
                    entries.append(entry)
            print "Entries created, loading into database..."

            locDistro.insert(entries)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接