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

时间:2016-03-07 03:59:34

标签: python mongodb mongodb-query pymongo

我正在尝试为拼图网站编写MongoDB后端。我对pymongo很新,我一直在努力寻找一种方法来检查一个唯一的密钥标识符,并在它退出时更新子文档。我的布局是这样的:

request.user

如果杰克已经存在,我希望这样做:

{
_id : Jack
"username": Jack
"puzzles": [
    {
        "name": puzName,
        "rank": rank,
        "date": puzDate,
        "Global Score": score,
        "Points": points
    }
],
"attempts": 1
}

要填充字段,我从现有的html中使用字段并使用Beautiful Soup。

{
_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
}

正如您所看到的,我使用用户名作为唯一标识文档的方式。到现在为止还挺好。检查数据库,用户信息填充正确。

现在我想查看一个用户是否已经存在,如果他们这样做,请更新“谜题”字段以包含该谜题并将更新增加1.我认为这可以检查存在,但它似乎不起作用,而是直接插入:

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 个答案:

答案 0 :(得分:2)

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