2017-09-28 11 views
2

私はnumberフィールドを持つ文書を持っています。プロセスでは、値がnumberのコレクションに含まれていないドキュメントが追加されますが、最初にそのドキュメントがある場合は、numberが存在するかどうかがチェックされます。連続値の範囲のクエリmongo

10543 22000への隙間から653から667とnumberに、0から234までnumbernumberを文書のコレクションを考えるには、235から652と668にその文書にインポートする必要が10542にnumberのために存在します。

コレクションに存在する連続した値の範囲を返すクエリを作成できますか? (22000から234と667から653と10543にすなわち0)この情報を

、私は即座に... 10542に652から235と668の間で不足している書類を記入し、22001で継続することを知っているだろう

+0

私はあなたの質問をよく理解していません。指定した範囲の存在数を照会しますか?または、指定した範囲の不足している数値を照会したいですか? – barbakini

+0

技術的にどちらかがあります。主なことは、各ドキュメントのクエリを避け、ドキュメントが存在するかどうかを確認することです。代わりに、すでに存在する次の範囲または存在しない次の範囲のいずれかを返すクエリが1つ必要です。 –

答えて

0

ワンこれに近づけるには、存在を確認したい範囲を事前に定義してから、集計操作を実行して、それらの範囲内の数値の数を得ることができます。

var ranges = [ 
    [0, 99], 
    [100, 199], 
    [200, 299] 
]; 

事前定義された範囲を与えられ、ちょうど3つの数字とテストコレクション、例えば取る:

db.test.aggregate([ 
    { 
     "$group": { 
      "_id": null, 
      "range0Count": { 
       "$sum": { 
        "$cond": [ 
         { 
          "$and": [ 
           { "$gte": [ "$number", 0 ] }, 
           { "$lte": [ "$number", 99 ] } 
          ] 
         }, 
         1, 
         0 
        ] 
       } 
      }, 
      "range1Count": { 
       "$sum": { 
        "$cond": [ 
         { 
          "$and": [ 
           { "$gte": [ "$number", 100 ] }, 
           { "$lte": [ "$number", 199 ] } 
          ] 
         }, 
         1, 
         0 
        ] 
       } 
      }, 
      "range2Count": { 
       "$sum": { 
        "$cond": [ 
         { 
          "$and": [ 
           { "$gte": [ "$number", 200 ] }, 
           { "$lte": [ "$number", 299 ] } 
          ] 
         }, 
         1, 
         0 
        ] 
       } 
      } 
     } 
    } 
]) 
を次のように

db.test.insert([ 
    { number: 1 }, 
    { number: 87 }, 
    { number: 200 } 
]) 

パイプライン実行するがあろう

これは次の結果をもたらすでしょう

{ 
    "_id" : null, 
    "range0Count" : 2.0, 
    "range1Count" : 0.0, 
    "range2Count" : 1.0 
} 
あなたはさらに次のようにグループのパイプライン演算子オブジェクトを抽出するために、レンジアレイ上減らす方法を使用してパイプラインをリファクタリングすることができます

var ranges = [ 
    [0, 99], 
    [100, 199], 
    [200, 299] 
]; 
var group = ranges.reduce(function(acc, range, idx) { 
    acc["$group"]["range" + idx + "Count"] = { 
     "$sum": { 
      "$cond": [ 
       { 
        "$and": [ 
         { "$gte": ["$number", range[0] ] }, 
         { "$lte": ["$number", range[1] ] } 
        ] 
       }, 
       1, 
       0 
      ] 
     } 
    }; 
    return acc; 
}, { "$group": { "_id": null } }); 

db.test.aggregate([group]) 

をあなたが望むようあなたが範囲をカスタマイズすることができます上記のテンプレートを使用し、その結果からカウントがない範囲を取得します。

+0

私は何をしようとしているのかを明確にするために質問を更新しました –

+0

詳細な応答に感謝しますが、実行時にどの範囲を確認するのか分かりません。現在、私のプロセスは0から始まり、見つからない文書を見つけて挿入するまで各文書をチェックします。たとえば、0から431までの文書があり、次に432から477までの間隙があり、さらにいくつかの文書/ギャップなどがあります。問合せが連続する文書のすべての範囲を返すことを望みます。最高の伝票番号などで続行してください。 –

0

あなたが範囲とは対照的に、不足しているバックすべての個別のIDを取得して受け入れることができる場合、これはあなたのクエリです:

collection.aggregate({ 
    $group: { 
     "_id": null, // group all documents into the same bucket 
     "numbers": 
     { 
      $push: "$number" // create an array of all "number" fields 
     } 
    } 
}, { 
    $project: { 
     "_id": 0, // get rid of the "_id" field - not really needed 
     "numbers": { 
      $setDifference: [ { // compute the difference between... 
       $range: [ 0, 10 ] // ... all numbers from 0 to 10 - adjust this to your needs... 
      }, "$numbers" ] // ...and the available values for "number" 
     } 
    } 
}) 

あり、この情報のうち、範囲を計算する方法があるが、私は感じこれを持っていますあなたの場合には必要ないかもしれません。

ここでは、離散的な数値から範囲に到達するためのいくつかの段階を追加した、より長いバージョンです。コードは正確ではなく、おそらく超高速ではありませんが、少なくとも作業...

collection.aggregate({ 
    $sort: { 
     "number": 1 // we need to sort in order to find ranges later 
    } 
}, 
{ 
    $group: { 
     "_id": null, // group all documents into the same bucket 
     "numbers": 
     { 
      $push: "$number" // create an array of all "number" fields 
     } 
    } 
}, { 
    $project: { 
     "_id": 0, // get rid of the "_id" field - not really needed 
     "numbers": { 
      $setDifference: [ { // compute the difference between... 
       $range: [ 0, 10 ] // ... all numbers from 0 to 10 - adjust this to your needs... 
      }, "$numbers" ] // ...and the available values for "number" 
     } 
    } 
}, 
{ 
    $project: { 
     "numbers": "$numbers", // ...we create two identical arrays 
     "numbers2": "$numbers" // ...by duplicating our missing numbers array 
    } 
}, 
{ 
    $unwind: "$numbers" // this will flatten one of the two created number arrays 
}, 
{ 
    $project: { 
     "number": "$numbers", 
     "precedingNumber": { 
      $arrayElemAt: [ 
       "$numbers2", // use the second (remaining) numbers array to find the previous number... 
       { $max: [0, { $add: [ { $indexOfArray: [ "$numbers2", "$numbers" ] }, -1 ] } ] } // ...which needs to sit in that sorted array at the position of the element we're looking at right now - 1 
      ] 
     }, 
     "followingNumber": { 
      $arrayElemAt: [ 
       "$numbers2", // use the second (remaining) numbers array to find the next number... 
       { $add: [ { $indexOfArray: [ "$numbers2", "$numbers" ] }, 1 ] } // ...which needs to sit in that sorted array at the position of the element we're looking at right now + 1 
      ] 
     } 
    } 
}, { 
    $project: { 
     "number": 1, // include number 
     "precedingInRange": { $cond: [ { $eq: [ { $add: [ "$number", -1 ] }, "$precedingNumber" ] }, true, false ] }, 
     "followingInRange": { $cond: [ { $eq: [ { $add: [ "$number", 1 ] }, "$followingNumber" ] }, true, false ] } 
    } 
}, { 
    $match: { 
     $or: [ // filter out all items that are inside a range (or rather: include only the outer items of each range) 
      { "precedingInRange": false }, 
      { "followingInRange": false } 
     ] 
    } 
}, { 
    $project: { // some beautification of the ouput to help deal with the data in your application 
     "singleNumber": { $cond: [ { $not: { $or: [ "$precedingInRange", "$followingInRange" ] } }, "$number", null ] }, 
     "startOfRange": { $cond: [ "$followingInRange", "$number", null ] }, 
     "endOfRange": { $cond: [ "$precedingInRange", "$number", null ] } 
    } 
}) 

UPDATE 2:

私はきれいに関与あまり魔法なしの範囲を取得する方法のより良い方法を発見した感覚を持っています

collection.aggregate({ 
    $sort: { 
     "number": 1 // we need to sort by numbers in order to be able to do the range magic later 
    } 
}, { 
    $group: { 
     "_id": null, // group all documents into the same bucket 
     "numbers": 
     { 
      $push: "$number" // create an array of all "number" fields 
     } 
    } 
}, { 
    $project: { 
     "numbers": { 
      $reduce: { 
       input: "$numbers", 
       initialValue: [], 
       in: { 
        "start": { 
         $concatArrays: [ 
          "$$value.start", 
          { 
           $cond: { // if preceding element in array of numbers is not "current element - 1" then add it, otherwise skip 
            if: { $ne: [ { $add: [ "$$this", -1 ] }, { $arrayElemAt: [ "$numbers", { $add: [ { $indexOfArray: [ "$numbers", "$$this" ] }, -1 ] } ] } ] }, 
            then: [ "$$this" ], 
            else: [] 
           } 
          } 
         ] 
        }, 
        "end": { 
         $concatArrays: [ 
          "$$value.end", 
          { 
           $cond: { // if following element in array of numbers is not "current element + 1" then add it, otherwise skip 
            if: { $ne: [ { $add: [ "$$this", 1 ] }, { $arrayElemAt: [ "$numbers", { $add: [ { $indexOfArray: [ "$numbers", "$$this" ] }, 1 ] } ] } ] }, 
            then: [ "$$this" ], 
            else: [] 
           } 
          } 
         ] 
        } 
       } 
      } 
     } 
    } 
}, { 
    $project: { 
     "ranges": { 
      $zip: { 
       inputs: [ "$numbers.start", "$numbers.end" ], 
      } 
     } 
    } 
}) 
+0

ありがとうございます! –

+0

私は何百万もの文書を扱っているので空の範囲の始めと終わりを返すだけですばらしいだろうし、効率的な方法ではない。 –

+0

更新された答えは印象的に複雑ですが、100,000を超える文書のギャップがある場合は完全に実行不可能です。また、解が最初と最後の要素を欠いている範囲を返すことにも注意してください。これはmongoと関係がなしであることは自明ではないようですが、うまくいけば、私はある時点で役に立つクエリで取り返します。アイデア@Dnicklessありがとう –