mongoDB 具有多个键值的 `upsert`
mongoDB `upsert` with multiple key values
我正在从 Amazon Mechanical Turk 中提取一些数据并将其保存在 mongodb 集合中。
我让多名工人重复每项任务,因为一点冗余有助于我检查工作质量。
每次我使用 boto AWS python interface 从亚马逊提取数据时,我都会获得一个包含所有已完成 HIT 的文件,并想将它们插入到集合中。
这是我要插入到 collection
中的 document
:
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
是该图像中人物的标识符(每张图像可能有多个)。
data_list
是我从AMT工人那里得到的数据
worker_id
、worker_exp
、assignment_id
是关于AMT worker和assignment的变量。
使用 boto 的连续拉取将包含相同的数据,但我不想在我的集合中有重复的文档。
我知道有两种可能的解决方案,但 none 完全适合我:
我可以在集合中搜索该文档,仅当不存在时才将其插入。但这会产生非常高的计算成本。
我可以使用 upsert 作为一种方法来确保仅当某个键尚未包含时才插入文档。但是所有包含的键都可以复制,因为该任务由多个工作人员重复。
第 2 部分注意:
- subj_id
、img_id
、data_list
可以重复,因为不同的工作人员注释相同的主题、图像并且可以提供相同的数据。
- worker_id
、worker_exp
、assignment_id
可以重复,因为工作人员在同一个任务中注释多个图像。
- 唯一独特之处在于所有这些字段的组合。
有没有一种方法可以插入 mongo_doc
只有在之前没有插入的情况下?
只要"all"这里你想做的是"insert"项那么你这里有几个选择:
在所有必填字段中创建一个 "unique" 索引并使用插入。简单地说,当值的组合与已经存在的东西相同时,将抛出 "duplicate key" 错误。这会阻止同一事物被添加两次,并且可以提醒您出现异常。这可能最好与 Bulk Operations API 和 "unordered" 标志一起用于操作。相同的 "unordered" 可用于 insert_many()
,但我个人更喜欢 Bulk API 的语法,因为它允许更好的构建和混合操作:
bulk = pymongo.bulk.BulkOperationBuilder(collection,ordered=False)
bulk.insert(document)
result = bulk.execute()
如果在调用 .execute()
之前使用了多个操作,那么所有操作都会立即发送到服务器并且只有 "one" 响应。使用 "unordered",无论 "duplicate" 键等错误如何,所有项目都会被处理,并且 "result" 包含任何失败项目的报告。
这里明显的 "cost" 是在所有字段上创建 "unique" 索引将使用相当多的 space 并增加显着的开销 "write"作为索引信息的操作必须像数据一样被写入。
将 "upsert" 功能与 $setOnInsert
结合使用。这允许您使用 "all required unique fields" 构造查询,以便 "search" 查找文档是否存在。标准 "upsert" 行为是在找不到文档的地方创建 "new" 文档。
$setOnInsert
添加的内容是,该语句中的所有字段 "set" 仅在出现 "upsert" 的地方应用。在常规 "match" 中,$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
或其他运算符来更新您 "want" 的任何内容,或者完全忽略它,只有 "inserts" 会在不匹配的地方出现。
你不能做的当然是做一些事情,比如将1
的值赋给$setOnInsert
中的一个字段,然后做一些事情,比如$inc
在其他操作上。这会在您尝试修改 "same path" 时产生冲突,并会引发错误。
在那种情况下,最好保留 $setOnInsert
块的 $inc
字段 "out",让它正常运行。 { "$inc": 1 }
只会在第一次提交时分配 1
。 $push
和其他运算符也是如此。
"cost" 再次分配索引,"need" 不是 "unique" 但可能应该是。如果没有索引,操作是 "scanning the collection" 以获得可能的匹配而不是更有效的索引。所以它不是"required",但在未指定索引的情况下,额外"writes"的成本通常超过"lookup"的成本。
与 "Bulk" 操作相结合的进一步优势在于,由于 $setOnInsert
的 "upsert" 方法在所有唯一键都在查询,这可以与 "ordered" 一起用于批处理,如所演示的那样。
当 "ordered" 在一批操作中,操作在添加的 "sequence" 中处理,所以如果 "first" 插入发生对你很重要committed 然后它是 preffferable "unordered",虽然并行执行速度更快,但当然不能保证按照它们被构造的相同顺序提交操作。
无论哪种方式,您都需要花费维护 "unique" 项目的多个键的任一种形式。可能要查看 "reduce" 索引成本的替代方法是查看将文档的 _id
字段替换为您考虑的所有值 "unique".
因为主键总是 "unique" 并且总是 "required" 这最小化了写 "additional indexes" 的 "cost" 并且可能是一个可以考虑的选项。 _id
不会 "need" 成为 ObjectId,并且由于它可以是复合对象,因此如果您有另一个唯一标识符,那么以这种方式使用它可能是明智的,避免进一步的唯一重复。
我正在从 Amazon Mechanical Turk 中提取一些数据并将其保存在 mongodb 集合中。
我让多名工人重复每项任务,因为一点冗余有助于我检查工作质量。
每次我使用 boto AWS python interface 从亚马逊提取数据时,我都会获得一个包含所有已完成 HIT 的文件,并想将它们插入到集合中。
这是我要插入到 collection
中的 document
:
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
是该图像中人物的标识符(每张图像可能有多个)。data_list
是我从AMT工人那里得到的数据worker_id
、worker_exp
、assignment_id
是关于AMT worker和assignment的变量。
使用 boto 的连续拉取将包含相同的数据,但我不想在我的集合中有重复的文档。
我知道有两种可能的解决方案,但 none 完全适合我:
我可以在集合中搜索该文档,仅当不存在时才将其插入。但这会产生非常高的计算成本。
我可以使用 upsert 作为一种方法来确保仅当某个键尚未包含时才插入文档。但是所有包含的键都可以复制,因为该任务由多个工作人员重复。
第 2 部分注意:
- subj_id
、img_id
、data_list
可以重复,因为不同的工作人员注释相同的主题、图像并且可以提供相同的数据。
- worker_id
、worker_exp
、assignment_id
可以重复,因为工作人员在同一个任务中注释多个图像。
- 唯一独特之处在于所有这些字段的组合。
有没有一种方法可以插入 mongo_doc
只有在之前没有插入的情况下?
只要"all"这里你想做的是"insert"项那么你这里有几个选择:
在所有必填字段中创建一个 "unique" 索引并使用插入。简单地说,当值的组合与已经存在的东西相同时,将抛出 "duplicate key" 错误。这会阻止同一事物被添加两次,并且可以提醒您出现异常。这可能最好与 Bulk Operations API 和 "unordered" 标志一起用于操作。相同的 "unordered" 可用于
insert_many()
,但我个人更喜欢 Bulk API 的语法,因为它允许更好的构建和混合操作:bulk = pymongo.bulk.BulkOperationBuilder(collection,ordered=False) bulk.insert(document) result = bulk.execute()
如果在调用
.execute()
之前使用了多个操作,那么所有操作都会立即发送到服务器并且只有 "one" 响应。使用 "unordered",无论 "duplicate" 键等错误如何,所有项目都会被处理,并且 "result" 包含任何失败项目的报告。这里明显的 "cost" 是在所有字段上创建 "unique" 索引将使用相当多的 space 并增加显着的开销 "write"作为索引信息的操作必须像数据一样被写入。
将 "upsert" 功能与
$setOnInsert
结合使用。这允许您使用 "all required unique fields" 构造查询,以便 "search" 查找文档是否存在。标准 "upsert" 行为是在找不到文档的地方创建 "new" 文档。$setOnInsert
添加的内容是,该语句中的所有字段 "set" 仅在出现 "upsert" 的地方应用。在常规 "match" 中,$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
或其他运算符来更新您 "want" 的任何内容,或者完全忽略它,只有 "inserts" 会在不匹配的地方出现。你不能做的当然是做一些事情,比如将
1
的值赋给$setOnInsert
中的一个字段,然后做一些事情,比如$inc
在其他操作上。这会在您尝试修改 "same path" 时产生冲突,并会引发错误。在那种情况下,最好保留
$setOnInsert
块的$inc
字段 "out",让它正常运行。{ "$inc": 1 }
只会在第一次提交时分配1
。$push
和其他运算符也是如此。"cost" 再次分配索引,"need" 不是 "unique" 但可能应该是。如果没有索引,操作是 "scanning the collection" 以获得可能的匹配而不是更有效的索引。所以它不是"required",但在未指定索引的情况下,额外"writes"的成本通常超过"lookup"的成本。
与 "Bulk" 操作相结合的进一步优势在于,由于 $setOnInsert
的 "upsert" 方法在所有唯一键都在查询,这可以与 "ordered" 一起用于批处理,如所演示的那样。
当 "ordered" 在一批操作中,操作在添加的 "sequence" 中处理,所以如果 "first" 插入发生对你很重要committed 然后它是 preffferable "unordered",虽然并行执行速度更快,但当然不能保证按照它们被构造的相同顺序提交操作。
无论哪种方式,您都需要花费维护 "unique" 项目的多个键的任一种形式。可能要查看 "reduce" 索引成本的替代方法是查看将文档的 _id
字段替换为您考虑的所有值 "unique".
因为主键总是 "unique" 并且总是 "required" 这最小化了写 "additional indexes" 的 "cost" 并且可能是一个可以考虑的选项。 _id
不会 "need" 成为 ObjectId,并且由于它可以是复合对象,因此如果您有另一个唯一标识符,那么以这种方式使用它可能是明智的,避免进一步的唯一重复。