检查_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 

为什么它没有正确检查?我如何更新子文档?

+0

而如果“用户”不存在?如何使用['update_one()'](https://api.mongodb.org/python/current/api/pymongo/collection.html#pymongo.collection.Collection.update_one)方法? – styvane

+0

所以如果用户(_id)不存在于集合中,那么计数应该是0.如果它是0,那么else中的insert()将被执行。这将在集合中创建一个新的_id和用户。我没有包含我的更新代码,因为我没有测试过它。我想让find()正常工作首先更重要 – jaybeatle

+0

您的find()查询是否返回任何文档?你也有一个错字。你正在使用'col.find()'而不是'coll.find()',也许这是罪魁祸首。但坦率地说,你不需要使用'coll.find()。count()''你可以使用'update_one()'方法并将'upsert'选项设置为'True',然后检查'upserted_id'的值和'modified_count'并相应地打印一条消息。 – styvane

好吧,因为你看起来对数据库一般都很陌生,所以它可能会引发你正确的事情是“找到”事情,然后“更新”和“保存”,而只是发送一个"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得到的只能每天在你的应用程序生命周期发生一次。无论您的应用程序实际在做什么,该连接都应该可用,并在该应用程序的整个生命周期中持续存在,直至完成或终止。

+0

感谢您的详细回复!我从其他人那里接受了这个项目,坦率地说,我有点无知。我阅读了大量的文档和示例,但是当有人直接解决问题时,它总是更容易! – jaybeatle