3

mongo からオブジェクトの単一のコレクションを返す必要がある状況がありますが、結果を取得するには 2 つのクエリを使用する必要があります。これらの結果はページ付けされるため、これらの結果の順序は重要です。

最初のクエリは次のとおりです: (カテゴリと価格帯に基づくリスト)

my_listings = MoListing.where(criteria_a)

2 番目のクエリは、最初のクエリの結果をフィルターとして使用する必要があります。次のようなものです:

everything_else = MoListing.where(criteria_b)

次に、結果を結合します。

my_listings << everything_else

最後に、ページ分割された結果を返します。

my_listings.page(1).per(25)

私の問題の一部は、必要になるまでmongoクエリが実行されないことです。特定の時点でクエリの実行をトリガーする方法はありますか? または、この結果セットを構築する際に取るべき別のアプローチはありますか?

詳細情報を更新

私が見ている動作は、返されるのは の結果だけlistingsです。everything_elseまた、予想されるレコードが含まれていることも確認しました (予想どおり、my_listings に 48 レコード、everything_else に 52 レコード)。

.allコメントに記載されているようにクエリに適用しても、影響はありません。のputs listings.inspect結果

10:57:00 web.1   |    #<Mongoid::Criteria
10:57:00 web.1   |    selector: {"price"=>{"$gte"=>25, "$lte"=>75}},
10:57:00 web.1   |    options:  {},
10:57:00 web.1   |    class:    MoListing,
10:57:00 web.1   |    embedded: false>

ただし、listings.count結果は48. これらの結果をマージする愚かで簡単な方法が欠けているだけですか? そして、結果を 1 つのコレクションにまとめたら、これが後続のページ付け機能にどのような影響を与えるでしょうか。ページネーションに使用kaminariしています。

更新 2

以下の回答と私自身の試行錯誤により、 to_a が解決策であることがわかりましたが、理想的なものではありません。これは機能します:

#merge the results together as an Array
results = (listings.to_a | everything_else.to_a)

これにより、mongo 基準ではなく標準の配列を使用するようになったため、Kaminari を介したページ付けを変更する必要があります。新しいページネーション方法は次のとおりです。

Kaminari.paginate_array(results).page(page).per(per_page)

100 レコードの小さなデータセットで作業すると、これは問題なくダンディです - 54 ミリ秒

"debug":{"success":true,"pre_render_duration":54.808775999999995,"overall_duration":86.36554100000001,"count":25},"pagination":{"total_pages":4,"current_page":1}}

ただし、より大きなデータセットを使用すると、 .to_a メソッドを使用してこれらを組み合わせると、時間が大幅に遅くなります。例は厳密には同じではありませんが、この大きな違いは、to_a がすべてを返すという問題を示しており、Kaminari はより多くの実際のデータを操作する必要があります。

to_a を使用しない結果、条件が適用されたすべてのレコードを返すだけ - 15ms

"debug":{"success":true,"pre_render_duration":15.107164,"overall_duration":18.267599,"count":25},"pagination":{"total_pages":81,"current_page":1}}

to_a での結果、2 つの結果セットのマージ - 415ms

"debug":{"success":true,"pre_render_duration":415.258199,"overall_duration":450.66537800000003,"count":25},"pagination":{"total_pages":81,"current_page":1}}

要約すると、これは有効なオプションではありません。各データセットを個別に返すには、大規模なデータセットでも 15 ミリ秒未満かかるため、達成する必要があるのは、単一のクエリが Mongo に対して実行されるように基準をマージする方法であると思います。なれ。

SQLでは、おおよそ次のようなことをします

select
  *
from
  listings
where
  field = "blah"
union all
select
  *
from
  listings
where
  field <> "blah"

Mongoでこれを行うことは可能ですか?

4

3 に答える 3

4

おそらく、その特定の配列のデータを取得する方法の詳細をカプセル化するクラスを作成し、Mongo ドライバーを使用してクエリ オプションをスキップおよび制限し、転送されるデータ サイズを減らすことができます。

このアプローチを使用すると、次のようなものを使用できます (ネーミングはあまり良くなく、コードをテストしていませんが、要点を理解してください):

class MoListingDataRetriever
  def initialize(page_size)
    @page_size = page_size / 2 #since you'll have two queries
    driver_instance = MoListing.db #just an exemple. You could use any of your classes that are mongo documents to do this
    @collection_driver = driver_instance.collection("mo_listing") #or whatever you collection name is on mongo
  end

  def retrieve_mo_listings(query_param_a, query_param_b, current_page)
    query_options = {
      limit: @page_size,
      page: current_page,
      skip: (@page_size * (current_page - 1)) #to skip a number of records already retrieved from the query
    }
    results_from_query_a = @driver_instance.find(query_param_a, query_options)
    results_from_query_b = @driver_instance.find(query_param_a, query_options)
    results_from_query_b.to_a.concat(results_from_query_b.to_a)    
  end
end
于 2012-09-13T17:32:57.143 に答える
2

ただし、これは大雑把な方法かもしれません。

# Let us say the listings is obtained using listing_query_params
listings = MoListing.where(listing_query_params)

# and everything else is from everything_else_query_params
everything_else = MoListing.where(everything_else_query_params)

results = [listings.to_a, everything_else.to_a].flatten

results.page(1).per(25)

これは、あなたの望むことですか?私は自分のモンゴイドモデルの1つでそれを試しましたが、このように機能しているようです。

PS:しかし、.to_aのパフォーマンスは低下します。結果セット全体がフェッチされ、マージされます。しかし、あなたが言及したレコードの数(1枚あたり約50)を見ると、それは問題ないはずです。

于 2012-09-10T22:28:36.123 に答える
0

これを試して:

my_listings = MoListing.where(criteria_a)
everything_else = MoListing.where(criteria_b)
all_listings = MoListing.or(my_listings.selector).or(everything_else.selector).page(1).per(25)
于 2015-01-15T12:52:14.957 に答える