0

各ユーザーは 1 つのアドレスを持ちます。

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ますか?

モンゴイド5

4

2 に答える 2

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_idAddress コレクションで呼び出された外部キー フィールドがあると仮定すると、一括更新ではないためです。次のことができる場合があります。

Address.collection.update({ 'q' => {'user_id' => user._id},
                            'u' => {'address' => 'address'},
                            'multi' => false,
                            'upsert' => true }

に対して一致し、user_id見つかった場合は指定されたフィールドを更新し (addressこの場合)、見つからない場合は新しいフィールドを作成します。

ただし、これが機能するためには、最後の重要なステップが 1 つあります。特別なフラグを使用して Address コレクションにインデックスを追加する必要があります。

クエリを実行するフィールド (user_idこの場合) は、 または のいずれかのフラグでインデックス化する必要があり{ unique: true } ます{ sparse: true }。2 つ以上の nilフィールドuniqueがある場合、フラグはエラーを発生させます。オプションはしませんuser_idsparsenil 値があると思われる場合は、それを使用してください。

ターミナルからmongo dbにアクセスします

  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 } )

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ではなく aが必要であることを示していますが、それらは同じようです..query

Address.collection.update_one( { user_id: user_id },
  '$set' => { "address": 'the_address', upsert: true} )

PS: asなど{ "address": 'the_address' }を含めずに update 句としてのみ記述すると、フィールドだけを更新するのではなく、ドキュメント全体が上書きされます。update operator$setaddress

編集#2

uniqueorで索引付けする理由についてsparse

upsert以下のリンクのセクションを見ると、次のことがわかります。

複数のアップサートを回避するには、フィルター フィールドが一意にインデックス化されていることを確認してください。

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

于 2016-09-07T21:00:07.917 に答える
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})
于 2016-09-07T20:20:16.040 に答える