我最近遇到了这个问题。有一个内置方法,但它不同于活动记录的 #find_or_initialize_by 或 #find_or_create_by 方法。
在我的例子中,我需要批量插入记录并更新或创建如果没有找到,但我相信即使你不是批量插入也可以使用相同的技术。
# returns an array of query hashes:
def update_command(users)
updates = []
users.each do |user|
updates << { 'q' => {'user_id' => user._id},
'u' => {'address' => 'address'},
'multi' => false,
'upsert' => true }
end
{ update: Address.collection_name.to_s, updates: updates, ordered: false }
end
def bulk_update(users)
client = Mongoid.default_client
command = bulk_command(users)
client.command command
client.close
end
由于您不是批量更新,假设您的地址集合中有一个名为 user_id 的外键字段。您或许可以:
Address.collection.update({ 'q' => {'user_id' => user._id},
'u' => {'address' => 'address'},
'multi' => false,
'upsert' => true }
将匹配user_id,在找到时更新给定字段(在本例中为address)或在未找到时创建一个新字段。
为此,还有最后一个关键步骤。
您必须使用特殊标志向您的地址集合添加索引。
您正在查询的字段(在本例中为user_id)
必须使用{ unique: true } 的标志进行索引
或{ sparse: true }。 unique 标志将引发错误
如果您有 2 个或更多 nil user_id 字段。 sparse 选项不会。
如果您认为您可能有 nil 值,请使用它。
通过终端访问您的 mongo 数据库
show dbs
-
use your_db_name
检查地址集合是否已有您要查找的索引
-
db.addresses.getIndexes()
如果它在 user_id 上已有索引,您可能需要删除它
db.addresses.dropIndex( { user_id: 1} )
并使用以下标志再次创建它:
db.addresses.createIndex( { user_id: 1}, { sparse: true } )
https://docs.mongodb.com/manual/reference/method/db.collection.update/
编辑#1
Mongoid 5 似乎发生了变化。你可以使用 User.collection.update 而不是 User.collection.update_one
https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/
文档显示您需要 filter 而不是 query 作为第一个参数,但它们似乎是相同的..
Address.collection.update_one( { user_id: user_id },
'$set' => { "address": 'the_address', upsert: true} )
PS:
如果您只写 { "address": 'the_address' } 作为更新子句而不包括 update operator,例如 $set,整个文档将被覆盖,而不是仅更新 address 字段。
编辑#2
关于您可能希望使用unique 或sparse 编制索引的原因
如果您查看下面链接中的upsert 部分,您会看到:
为避免多次更新插入,请确保过滤器字段是唯一的
索引。
https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/