我最近遇到了这个问题。有一种内置方式,但它与活动记录#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 }
or标志进行索引{ sparse: true }
。如果您有 2 个或更多 nil字段,该unique
标志将引发错误。user_id
该sparse
选项不会。如果您认为您可能有 nil 值,请使用它。
通过终端访问您的 mongo db
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/
文档显示您需要 afilter
而不是 aquery
作为第一个参数,但它们似乎是相同的..
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/