我从Amazon Mechanical Turk中提取一些数据并将其保存在mongodb集合中。
我有多个工作人员重复每项任务,因为一点点冗余可以帮助我检查工作质量。
每次我使用boto AWS python interface从亚马逊中提取数据时,我都会获得一个包含所有已完成的HIT的文件,并希望将它们插入到集合中。
以下是我要插入document
的{{1}}:
collection
mongo_doc = \
{'subj_id' :data['subj_id'],
'img_id' :trial['img_id'],
'data_list' :trial['data_list'],
'worker_id' :worker_id,
'worker_exp' :worker_exp,
'assignment_id':ass_id
}
是图像数据库中图像的标识符。img_id
是该图片中人物的标识符(每张图片可能有多个)。subj_id
是我从AMT工作人员那里获得的数据。data_list
,worker_id
,worker_exp
是有关AMT工作人员和作业的变量。使用boto的连续提取将包含相同的数据,但我不想在我的收藏中包含重复的文档。
我知道两种可能的解决方案,但没有一种方法适合我:
我可以在集合中搜索文档,只有在不存在时插入它。但这会产生很高的计算成本。
我可以使用upsert作为确保仅在尚未包含某个键时才插入文档的方法。但是,由于多个工作人员重复执行任务,因此可以复制所有包含的密钥。
第2部分的注意事项:
- assignment_id
,subj_id
,img_id
可以重复,因为不同的工作人员注释相同的主题,图像并可以提供相同的数据。
- data_list
,worker_id
,worker_exp
可以复制,因为工作人员在同一作业中注释多个图像。
- 唯一独特的是所有这些领域的组合。
如果以前没有插入assignment_id
,是否可以插入mongo_doc
答案 0 :(得分:2)
只要你想在这里做的“全部”是“插入”项目,那么你在这里有几个选择:
在所有必填字段中创建“唯一”索引并使用insert。简单地说,当值的组合与已存在的值相同时,将抛出“重复键”错误。这会阻止同样的事情被添加两次并且可以提示异常。这可能最适合用于操作的Bulk Operations API和“无序”标志。 insert_many()
可以使用相同的“无序”,但我个人更喜欢Bulk API的语法,因为它允许更好的构建和混合操作:
bulk = pymongo.bulk.BulkOperationBuilder(collection,ordered=False)
bulk.insert(document)
result = bulk.execute()
如果在调用.execute()
之前使用了多个操作,则所有操作都会立即发送到服务器,并且只有“一个”响应。使用“无序”时,所有项目都会被处理,例如“重复”键,而“结果”包含任何失败项目的报告。
这里显而易见的“成本”是在所有字段上创建“唯一”索引将使用相当大的空间以及为“写入”操作增加大量开销,因为必须写入索引信息以及数据。
对$setOnInsert
使用“upsert”功能。这允许您使用“所有必需的唯一字段”构造查询,以便“搜索”文档以查看是否存在。标准的“upsert”行为位于未找到文档的位置,然后创建“新”文档。
$setOnInsert
添加的内容是,该语句中“set”的所有字段仅应用于发生“upsert”的位置。在常规“匹配”上,$setOnInsert
内的所有作业都将被忽略:
bulk = pymongo.bulk.BulkOperationBuilder(collection,ordered=True)
bulk.find({
"subj_id": data["subj_id"],
"img_id": data["img_id"]
"data_list": data["data_list"],
"worker_id": data["worker_id"],
"worker_exp": data["worker_exp"],
"assignment_id": data["assignment_id"]
}).upsert().update_one({
"$setOnInsert": {
# Just the "insert" fields or just "data" as an object for all
"subj_id": data["subj_id"],
"img_id": data["img_id"]
"data_list": data["data_list"],
"worker_id": data["worker_id"],
"worker_exp": data["worker_exp"],
"assignment_id": data["assignment_id"]
},
"$set": {
# Any other fields "if" you want to update on match
}
})
result = bulk.execute()
根据您的需要,如果文档匹配,您可以使用$set
或其他操作符来“更新”您要“更新”的内容,或者将其完全保留,只有“插入”才会匹配。< / p>
你不能当然要做的事情就是为1
内的字段指定$setOnInsert
的值,然后在其他字段上执行$inc
之类的操作操作。这会产生冲突,您尝试修改“相同路径”并抛出错误。
在这种情况下,最好将$inc
字段“移出”$setOnInsert
块,然后让它正常进行操作。 { "$inc": 1 }
只会在第一次提交时分配1
。同样适用于$push
和其他运营商。
“成本”再次作为一个指数,它不需要“独特”但可能应该是。如果没有索引,操作将“扫描集合”以获得可能的匹配,而不是更有效的索引。所以它不是“必需的”,但是在没有指定索引的情况下,额外“写入”的成本通常会超过“查找”的成本。
与“批量”操作结合使用时的另一个优点是,由于$setOnInsert
的“upsert”方法在查询中包含所有唯一键时不会抛出任何“重复键”错误,因此可以使用如所示,“有序”写入批次。
当在一批操作中“命令”时,操作将在添加它们的“序列”中处理,因此如果对您来说重要的是“第一个”插入发生的是那个被调用的那个,那么它是“无序”是一种令人厌恶的,它虽然能够更快地并行执行,但当然不能保证按照构造它们的顺序提交操作。
无论哪种方式,您都需要使用任一表单在多个键上维护“唯一”项目。可能另一种看待“降低”索引成本的方法是,用您认为“独特”的所有值替换文档的_id
字段。
由于该主键始终是“唯一的”并且始终是“必需的”,因此最小化了编写“附加索引”的“成本”,并且可能是一个需要考虑的选项。 _id
并不“需要”成为ObjectId,因为它可以是一个复合对象,那么如果你有另一个唯一标识符,那么以这种方式使用它可能是明智的,避免进一步的唯一复制。