一般的に、あなたが説明しているのは、MongoDBコミュニティで比較的よくある質問であり、「トップn
」と表現できます。 結果の問題」。これは、何らかの方法で並べ替えられている可能性のある入力が与えられた場合に、上位のn
を取得する方法です。 データ内の任意のインデックス値に依存せずに結果が得られます。
MongoDBには、 $first
があります。
集約フレームワーク
で使用できる演算子 これは、問題の「トップ1」の部分を処理します。これは、実際には、「タイプ」などのグループ化境界で見つかった「最初の」アイテムを取得するためです。しかし、もちろん、「1つ」以上の結果を得るには、もう少し複雑になります。 n
を処理するために他の演算子を変更することに関して、これにはいくつかのJIRAの問題があります。 結果または「制限」または「スライス」。特に
MongoDBストレージのrailsActiveRecordパターンの一般的な実装は、Mongoid
です。 および.collection
を介して「ネイティブ」mongodbコレクション関数へのアクセスを許可します アクセサー。これは、基本的に .aggregate()
これは、一般的なActiveRecord集約よりも多くの機能をサポートします。
これがmongoidを使用した集約アプローチですが、ネイティブコレクションオブジェクトにアクセスできるようになると、一般的なコードは変更されません。
require "mongoid"
require "pp";
Mongoid.configure.connect_to("test");
class Item
include Mongoid::Document
store_in collection: "item"
field :type, type: String
field :pos, type: String
end
Item.collection.drop
Item.collection.insert( :type => "A", :pos => "First" )
Item.collection.insert( :type => "A", :pos => "Second" )
Item.collection.insert( :type => "A", :pos => "Third" )
Item.collection.insert( :type => "A", :pos => "Forth" )
Item.collection.insert( :type => "B", :pos => "First" )
Item.collection.insert( :type => "B", :pos => "Second" )
Item.collection.insert( :type => "B", :pos => "Third" )
Item.collection.insert( :type => "B", :pos => "Forth" )
res = Item.collection.aggregate([
{ "$group" => {
"_id" => "$type",
"docs" => {
"$push" => {
"pos" => "$pos", "type" => "$type"
}
},
"one" => {
"$first" => {
"pos" => "$pos", "type" => "$type"
}
}
}},
{ "$unwind" => "$docs" },
{ "$project" => {
"docs" => {
"pos" => "$docs.pos",
"type" => "$docs.type",
"seen" => {
"$eq" => [ "$one", "$docs" ]
},
},
"one" => 1
}},
{ "$match" => {
"docs.seen" => false
}},
{ "$group" => {
"_id" => "$_id",
"one" => { "$first" => "$one" },
"two" => {
"$first" => {
"pos" => "$docs.pos",
"type" => "$docs.type"
}
},
"splitter" => {
"$first" => {
"$literal" => ["one","two"]
}
}
}},
{ "$unwind" => "$splitter" },
{ "$project" => {
"_id" => 0,
"type" => {
"$cond" => [
{ "$eq" => [ "$splitter", "one" ] },
"$one.type",
"$two.type"
]
},
"pos" => {
"$cond" => [
{ "$eq" => [ "$splitter", "one" ] },
"$one.pos",
"$two.pos"
]
}
}}
])
pp res
ドキュメントの名前は実際にはコードで使用されていません。「First」、「Second」などに表示されるデータのタイトルは、実際にリストから「トップ2」のドキュメントを取得していることを示すためのものです。結果。
したがって、ここでのアプローチは、基本的に、「タイプ」などのキーによって「グループ化」されたドキュメントの「スタック」を作成することです。ここで最初に行うことは、 $first
オペレーター。
以降の手順では、スタックから「表示された」要素を照合してフィルタリングし、 $first
オペレーター。そこにある最後のステップは、実際には、入力で見つかった元の形式にドキュメントを戻すためのjustxです。これは、通常、そのようなクエリから期待されるものです。
したがって、結果はもちろん、各タイプの上位2つのドキュメントのみです。
{ "type"=>"A", "pos"=>"First" }
{ "type"=>"A", "pos"=>"Second" }
{ "type"=>"B", "pos"=>"First" }
{ "type"=>"B", "pos"=>"Second" }
この最近の回答には、これと他の解決策のより長い議論とバージョンがありました:
Mongodbアグリゲーション$group、配列の長さを制限
タイトルにもかかわらず本質的に同じことであり、その場合は最大10以上の上位エントリに一致することを探していました。そこには、より大きな一致を処理するためのパイプライン生成コードや、データに応じて検討される可能性のあるいくつかの代替アプローチもあります。