Python 使用和更新子文档Pymongo检查_id是否存在

Python 使用和更新子文档Pymongo检查_id是否存在,python,mongodb,mongodb-query,pymongo,Python,Mongodb,Mongodb Query,Pymongo,我目前正在尝试为一个益智网站编写一个MongoDB后端。我是pymongo的新手,我一直在努力寻找一种方法来检查唯一的密钥标识符,并在子文档退出时更新它。我的布局如下: { _id : Jack "username": Jack "puzzles": [ { "name": puzName, "rank": rank, "date": puzDate, "Global Score": score, "Poin

我目前正在尝试为一个益智网站编写一个MongoDB后端。我是pymongo的新手,我一直在努力寻找一种方法来检查唯一的密钥标识符,并在子文档退出时更新它。我的布局如下:

{
_id : Jack
"username": Jack
"puzzles": [
    {
        "name": puzName,
        "rank": rank,
        "date": puzDate,
        "Global Score": score,
        "Points": points
    }
],
"attempts": 1
}
如果Jack已经存在,我希望它这样做:

{
_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中提取字段,并使用BeautifulSoup

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

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

既然您通常对数据库不熟悉,那么您可能会觉得正确的做法不是“查找”内容,然后“更新”和“保存”,而是只发送请求:

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
)
因此,这些“更新”通过查找与
\u id
值匹配的文档,然后考虑以下操作来工作:

  • 包含将添加到数组字段的内容。因此,任何新内容都将“附加”到名为
    “谜题”
    的文档中的数组中

  • 将查看文档中
    “尝试次数”
    的当前值,然后根据作为参数提供的任何值“递增”该值

  • 是特殊的,它不是对匹配的每个文档进行更改,而是只在发生
    upsert
    时进行提供的修改

  • 当然是最终设置,这意味着如果
    \u id
    值不匹配,则将使用用于查找文档的
    \u id
    值以及
    $setOnInsert
    中提到的任何内容创建新文档

当然,每个匹配的文档或创建的文档都会受到其他
$push
$inc
操作的影响,因此这些操作将始终应用于现有内容或添加到匹配文档中已找到的内容中

在最佳情况下,在循环数据源时,最好将此类“写入”提交到中的数据库,而不是一次只发送一个操作:

# 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或更少),而不是像idividual写入那样

但无论如何,没有必要将数据作为单独的请求进行“查找”,因为这是“更新”尤其是“升级”要处理的。原子操作允许“就地”修改数据,因此在进行更改之前无需阅读文档内容



还请注意,“连接”,例如通过
MongoClient
获得的连接,在应用程序生命周期中应该只发生一次。无论您的应用程序实际在做什么,该连接都应该是可用的,并在该应用程序的整个生命周期内保持,直到它运行完成或终止。

如果“用户”不存在怎么办?使用该方法如何?因此,如果集合中不存在用户(_id),则计数应为0。如果为0,则将执行else中的insert()。这将在集合中创建一个新的_id和用户。我没有包括我的更新代码,因为我没有测试它。我认为首先让find()正常工作更重要的是
find()
query是否返回任何文档?还有你的打字错误。您使用的是
coll.find()
而不是
coll.find()
,这可能是罪魁祸首。但是坦率地说,你不需要使用
coll.find().count()
你只需要使用
update\u one()
方法,将
upsert
选项设置为
True
,然后检查
upserted\u id
modified\u count
的值,并相应地打印一条消息。你需要操作员。谢谢,我会尝试的!感谢您的详细回复!我从别人那里接手了这个项目,坦率地说,我有点不知所措。我阅读了大量的文档和示例,但如果有人直接解决问题,这通常会更容易!