12

RailsでMongoDBを操作するためにMongoidを使用しています。

私が探しているのはアクティブレコードのようなものですinclude。現在、私はそのような方法をモンゴイドormで見つけることができませんでした。

誰もがこの問題をmongoidまたはおそらくmongomapperで解決する方法を知っています。これは、別の優れた代替手段として知られています。

4

8 に答える 8

17

しばらく経ちましたが、Mongoidは確かにこれに対するサポートを追加しました。こちらの「EagerLoading」セクションをご覧ください:http:
//docs.mongodb.org/ecosystem/tutorial/ruby-mongoid-tutorial/#eager-loading

Band.includes(:albums).each do |band|
  p band.albums.first.name # Does not hit the database again.
end

私が指摘したいのは:

  1. Rails:includeは参加しません
  2. SQLとMongoはどちらも積極的な読み込みが必要です。
  3. N + 1の問題は、このタイプのシナリオ(ループ内で生成されたクエリ)で発生します。

<% @posts.each do |post| %>
  <% post.comments.each do |comment| %>
    <%= comment.title %>
  <% end %>
<% end %>

@amrntが投稿したリンクがMongoidにマージされたようです。

于 2011-10-11T21:42:47.177 に答える
12

更新:この回答を投稿してから2年が経ち、状況が変わりました。詳細については、 tybro0103の回答を参照してください。


古い答え

両方のドライバーのドキュメントに基づいて、どちらもあなたが探しているものをサポートしていません。おそらくそれは何も解決しないからです。

ActiveRecordの機能は、 SQLデータベース:includeのN+1の問題を解決します。含める関連テーブルをActiveRecordに指示することで、ステートメントを使用して単一のSQLクエリを作成できます。これにより、クエリするテーブルの量に関係なく、単一のデータベース呼び出しが発生します。JOIN

MongoDBでは、一度に1つのコレクションのみをクエリできます。のようなものはサポートしていませんJOIN。したがって、Mongoidに含める必要のある他のコレクションを指定できたとしても、追加のコレクションごとに個別のクエリを実行する必要があります。

于 2010-10-12T08:39:56.170 に答える
8

他の答えは正しいですが、Mongoidの現在のバージョンでは、includesメソッドが目的の結果を達成するための最良の方法です。インクルードが利用できなかった以前のバージョンでは、n + 1の問題を取り除く方法を見つけ、言及する価値があると思いました。

私の場合、それはn+2の問題でした。

class Judge
  include Mongoid::Document

  belongs_to :user
  belongs_to :photo

  def as_json(options={})
    {
      id: _id,
      photo: photo,
      user: user
    }
  end
end

class User
  include Mongoid::Document

  has_one :judge
end

class Photo
  include Mongoid::Document

  has_one :judge
end

コントローラのアクション:

def index
  @judges = Judge.where(:user_id.exists => true)
  respond_with @judges
end

このas_json応答により、Judgeレコードからn+2クエリの問題が発生します。私の場合、開発サーバーに次の応答時間を与えます。

816msで200OKを完了(ビュー:785.2ms)

この問題を解決するための鍵は、審査員ごとに1つずつではなく、1つのクエリでユーザーと写真を読み込むことです。

これは、 Mongoids IdentityMapMongoid2およびMongoid3がこの機能をサポートしていることを利用して行うことができます。

まず、mongoid.yml構成ファイルでIDマップをオンにします。

development:
  host: localhost
  database: awesome_app
  identity_map_enabled: true

次に、コントローラーのアクションを変更して、ユーザーと写真を手動でロードします。注:Mongoid :: Relationshipレコードはクエリを遅延評価するため、to_aを呼び出して実際にレコードをクエリし、IdentityMapに保存する必要があります。

def index
  @judges ||= Awards::Api::Judge.where(:user_id.exists => true)
  @users = User.where(:_id.in => @judges.map(&:user_id)).to_a
  @photos = Awards::Api::Judges::Photo.where(:_id.in => @judges.map(&:photo_id)).to_a
  respond_with @judges
end

これにより、合計3つのクエリのみになります。審査員用に1つ、ユーザー用に1つ、写真用に1つ。

559msで200OKを完了(ビュー:87.7ms)

これはどのように作動しますか?IdentityMapとは何ですか?

IdentityMapは、どのオブジェクトまたはレコードがすでにロードされているかを追跡するのに役立ちます。したがって、最初のユーザーレコードをフェッチすると、IdentityMapはそれを保存します。次に、同じユーザーを再度フェッチしようとすると、Mongoidはデータベースに再度クエリを実行する前にユーザーのIdentityMapにクエリを実行します。これにより、データベースに1つのクエリが保存されます。

したがって、すべてのユーザーと写真をロードすることで、手動クエリで裁判官jsonに必要なことがわかっているので、データを一度にIdentityMapにプリロードします。次に、裁判官がユーザーと写真を要求すると、IdentityMapをチェックし、データベースにクエリを実行する必要はありません。

于 2012-10-23T06:22:22.537 に答える
5

ActiveRecord:includeは通常、Rubyオブジェクトにデータを取り込むために完全な結合を行いません。2回の呼び出しを行います。最初に親オブジェクト(たとえば投稿)を取得し、次に関連オブジェクト(投稿に属するコメント)をプルするための2番目の呼び出し。

Mongoidは、参照される関連付けに対して基本的に同じように機能します。

def Post
    references_many :comments
end

def Comment
    referenced_in :post
end

コントローラでは、次の投稿を取得します。

@post = Post.find(params[:id])

あなたの見解では、コメントを繰り返します。

<%- @post.comments.each do |comment| -%>
    VIEW CODE
<%- end -%>

Mongoidはコレクション内の投稿を検索します。コメントイテレータを押すと、コメントを取得するために単一のクエリが実行されます。Mongoidはクエリをカーソルでラップするため、真のイテレータであり、メモリが過負荷になることはありません。

Mongoid lazyはすべてのクエリをロードして、デフォルトでこの動作を許可します。:includeタグは不要です。

于 2010-12-03T16:58:39.783 に答える
1

これはhttps://github.com/flyerhzm/mongoid-eager-loadingに役立つ可能性があります

于 2011-06-29T13:56:42.283 に答える
0

このN+1を回避するには、スキーマを更新する必要があります。MongoDBには、いくつかの結合を行うためのソリューションがありません。

于 2010-10-12T09:39:43.333 に答える
0

詳細レコード/ドキュメントをマスターレコード/ドキュメントに埋め込みます。

于 2010-10-13T18:50:15.233 に答える
0

私の場合、私はコレクション全体を持っていませんでしたが、n + 1を引き起こしたそのオブジェクトを持っていました(箇条書きはそれを言います)。

したがって、n+1を引き起こす以下に書くのではなく

quote.providers.officialname

私が書いた

Quote.includes(:provider).find(quote._id).provider.officialname

それは問題にはなりませんでしたが、私が自分自身を繰り返すか、n+1をチェックすることはmongoidには不要であるかどうかを考えさせられました。

于 2014-07-29T23:11:07.100 に答える