什么是Mongoid中`update_or_initialize_with`的好方法?

时间:2016-09-07 19:57:40

标签: ruby-on-rails ruby mongodb mongoid mongoid5

每个用户都有一个地址。

class User
  include Mongoid::Document

  has_one :address
end

class Address
  include Mongoid::Document

  belongs_to :user

  field :street_name, type:String
end

u = User.find(...)
u.address.update(street_name: 'Main St')

如果我们的用户没有地址,则会失败。

那么,是否有一种很好的(内置)方式来做u.address.update_or_initialize_with

Mongoid 5

2 个答案:

答案 0 :(得分:0)

我不熟悉红宝石。但我想我理解这个问题。您的架构可能如下所示。

user = {
   _id : user1234,
   address: address789
} 

address = {
   _id: address789,
   street_name: ""
   user: user1234
}

//in mongodb(javascript), you can get/update address of user this way
u = User.find({_id: user1234})
u.address //address789
db.address.update({user: u.address}, {street_name: "new_street name"})

//but since the address has not been created, the variable u does not even have property address.
u.address = undefined

也许你可以试着像这样手动创建和附加它:

 #create an address document, to get _id of this address
 address = address.insert({street_name: "something"}); 

 #link or attached it to u.address
 u.update({address: address._id})

答案 1 :(得分:0)

我最近遇到了这个问题。有内置的方式,但它不同于活动记录' #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)或在找不到时创建新字段。

为了实现这一点,尽管有最后一个关键步骤。 您必须使用特殊标志向Address集合添加索引。

您要查询的字段(在这种情况下为user_id) 必须使用{ unique: true }的标记编入索引 或{ sparse: true }unique标志会引发错误 如果您有2个或更多nil user_id个字段。 sparse选项不会。 如果您认为自己可能没有值,请使用它。

通过终端访问您的mongo数据库

  1. show dbs
  2. use your_db_name 检查地址集合是否已具有您要查找的索引
  3. db.addresses.getIndexes() 如果它已经有user_id的索引,您可能想要删除它 db.addresses.dropIndex( { user_id: 1} ) 并使用以下标志再次创建它:
  4. db.addresses.createIndex( { user_id: 1}, { sparse: true } )
  5. 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字段。

    EDIT#2

    关于您可能希望使用uniquesparse

    进行索引的原因

    如果您查看以下链接中的upsert部分,您会看到:

      

    要避免多次upsert,请确保过滤器字段是唯一的   索引。

    https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/