检查_id是否存在使用和更新子文档Pymongo
我目前正在尝试编写一个拼图网站的MongoDB后端。我对pymongo相当陌生,我一直在努力寻找一种方法来检查唯一的密钥标识符,并在子文档退出时更新它。我的布局是这样的:检查_id是否存在使用和更新子文档Pymongo
{
_id : Jack
"username": Jack
"puzzles": [
{
"name": puzName,
"rank": rank,
"date": puzDate,
"Global Score": score,
"Points": points
}
],
"attempts": 1
}
如果杰克已经存在,我想它这样做:
{
_id : Jack
"username": Jack
"puzzles": [
{
"name": puzName,
"rank": rank,
"date": puzDate,
"Global Score": score,
"Points": points
}
{
"name": puzName2,
"rank": rank,
"date": puzDate,
"Global Score": score,
"Points": points
}
],
"attempts": 2
}
要填充字段,我是从现有的HTML和使用美丽的汤服用领域。
cells = row('td')
rank = cells[0].string
name = cells[1].find_all('a')[1].find(text=True).strip()
score = row('td')[3].string
points = row('td')[4].string
puz_dict = {}
puz_dict['_id'] = name.encode('ascii','ignore')
puz_dict['username'] = name.encode('ascii','ignore')
puz_dict['puzzles'] = {'Puzzle Name': puzName, 'Rank': int(str(rank)), "Date": puzDate,'Global Score' : locale.atoi(str(score)), 'Points' : int(str(points)) }
puz_dict['attempts'] = 1
connection = MongoClient('localhost')
coll = connection['Puzzles']['Users']
if col.find({'_id' : puz_dict['_id']}).count() > 0:
Print "Updating User"
update stuff
else:
coll.insert(puz_dict)
正如您所见,我使用用户名作为唯一标识文档的方式。到现在为止还挺好。检查数据库,用户信息正确填充。
现在我想检查用户是否已经存在,如果他们这样做,更新“拼图”字段以包含该拼图并将更新增加1.我认为这可以检查存在,但它似乎并没有工作,而是直接插入:
if col.find({'_id' : puz_dict['_id']}).count() > 0:
Print "Updating User"
update stuff
为什么它没有正确检查?我如何更新子文档?
好吧,因为你看起来对数据库一般都很陌生,所以它可能会引发你正确的事情是“找到”事情,然后“更新”和“保存”,而只是发送一个"update"请求:
coll = connection['Puzzles']['Users']
# after each assignment
coll.update_one(
{ "_id": puz_dict["_id"] },
{
"$setOnInsert": { "username": puz_dict["username"] },
"$push": { "puzzles": puz_dict["puzzles"] },
"$inc": { "attempts": puz_dict["attempts"] }
},
upsert = True
)
因此,这些“更新”工作通过查找该_id
值相匹配的文档,然后考虑采取以下行动:
$push
包含将被添加到一个数组字段的内容。因此,任何新内容都将被附加到名为"puzzles"
的文档中的数组中。$inc
将查看文档中的当前值"attempts"
,然后通过提供的任何值作为参数“增加”该值。$setOnInsert
是特殊的,而不是对每个匹配的文档进行更改,而只是在出现upsert
时进行提供的修改。upsert
当然终凝,这意味着其中_id
值不匹配,那么一个新的文档将被代替创建具有该被用来寻找文档该_id
值,然后任何内容的那在$setOnInsert
中被提及。
当然的每一个匹配的文件或创建的文档会受到其他$push
和$inc
操作,所以这些将始终适用,无论是对现有的内容或通过增加以匹配已找到的内容文件。
在最好的情况下,循环数据源时,最好是在"bulk"犯这种“写”到数据库中,而不是仅仅一次发送的每个操作之一:
# import the UpdateOne bulk helper
from pymongo import UpdateOne
# Outside loop of writing sourcing data
operations = []
# Inside loop of sourcing data, add to the queue
operations.append(
UpdateOne(
{ "_id": puz_dict["_id"] },
{
"$setOnInsert": { "username": puz_dict["username"] },
"$push": { "puzzles": puz_dict["puzzles"] },
"$inc": { "attempts": puz_dict["attempts"] }
},
upsert = True
)
)
# Only write to server 1 in 1000 and clear the queue
if (len(operations) % 1000 == 0):
coll.bulk_write(operations)
operations = []
# Finish the loop
# Then only write again if there will still queued operations
# remaining on loop completion
if (len(operations) > 0):
coll.bulk_write(operations)
这基本上是如何你可以通过为每一行细节添加操作作为输入,然后一次写入多个操作(理想情况下可能与驱动程序一致为1000或更少),而不是单独写入。
但无论如何,没有必要“查询”数据作为单独的请求,因为这是什么“更新”特别是“upserts”是要处理的。原子操作允许“就地”修改数据,因此在更改之前不需要阅读文档内容。
还要注意的是“关系”,如MongoClient
得到的只能每天在你的应用程序生命周期发生一次。无论您的应用程序实际在做什么,该连接都应该可用,并在该应用程序的整个生命周期中持续存在,直至完成或终止。
感谢您的详细回复!我从其他人那里接受了这个项目,坦率地说,我有点无知。我阅读了大量的文档和示例,但是当有人直接解决问题时,它总是更容易! – jaybeatle
而如果“用户”不存在?如何使用['update_one()'](https://api.mongodb.org/python/current/api/pymongo/collection.html#pymongo.collection.Collection.update_one)方法? – styvane
所以如果用户(_id)不存在于集合中,那么计数应该是0.如果它是0,那么else中的insert()将被执行。这将在集合中创建一个新的_id和用户。我没有包含我的更新代码,因为我没有测试过它。我想让find()正常工作首先更重要 – jaybeatle
您的find()查询是否返回任何文档?你也有一个错字。你正在使用'col.find()'而不是'coll.find()',也许这是罪魁祸首。但坦率地说,你不需要使用'coll.find()。count()''你可以使用'update_one()'方法并将'upsert'选项设置为'True',然后检查'upserted_id'的值和'modified_count'并相应地打印一条消息。 – styvane