2016-10-04 2 views
2

я сейчас работаю над базой данных со следующей структурой:MongoDB агрегат - в среднем на конкретных значений в массиве документов

{"_id" : ObjectId("1abc2"), 
"startdatetime" : ISODate("2016-09-11T18:00:37Z"), 
"diveValues" : [ 
    { 
     "temp" : 15.269, 
     "depth" : 0.0, 
    }, 
    { 
     "temp" : 14.779257384, 
     "depth" : 1.0, 
    }, 
    { 
     "temp" : 14.3940253165, 
     "depth" : 2.0, 
    }, 
    { 
     "temp" : 13.9225795455, 
     "depth" : 3.0, 
    }, 
    { 
     "temp" : 13.8214431818, 
     "depth" : 4.0, 
    }, 
    { 
     "temp" : 13.6899553571, 
     "depth" : 5.0, 
    } 
]} 

База данных содержит информацию о глубине н метров в воде, а температура на данный глубина. Это сохраняется в массиве «diveValues». Я добился успеха на усреднении по всем глубинам между датами, как среднемесячными, так и среднесуточными. То, что у меня серьезная проблема, заключается в том, чтобы получить среднее значение между глубинами, скажем, от 1 до 4 метров, за каждый месяц последних 6 месяцев.

Вот пример средней температуры за каждый месяц с января по июнь, на всех глубинах:

db.collection.aggregate(
    [ 
    {$unwind:"$diveValues"}, 
    {$match: 
     {'startdatetime': 
     {$gt:new ISODate("2016-01-10T06:00:29Z"), 
     $lt:new ISODate("2016-06-10T06:00:29Z")} 
     } 
     }, 

    {$group: 
     {_id: 
     { year: { $year: "$startdatetime" }, 
     month: { $month: "$startdatetime" }}, 
     avgTemp: { $avg: "$diveValues.temp" }} 
    }, 
    {$sort:{_id:1}} 
    ] 
) 

Результирующее в:

{ "_id" : { "year" : 2016, "month" : 1 }, "avgTemp" : 7.575706502958313 } 
{ "_id" : { "year" : 2016, "month" : 3 }, "avgTemp" : 6.85037457740135 } 
{ "_id" : { "year" : 2016, "month" : 4 }, "avgTemp" : 7.215702831902588 } 
{ "_id" : { "year" : 2016, "month" : 5 }, "avgTemp" : 9.153453683614638 } 
{ "_id" : { "year" : 2016, "month" : 6 }, "avgTemp" : 11.497953009390237 } 

Теперь я не могу показаться, чтобы выяснить, как для получения средней температуры от 1 до 4 метров за тот же период.

Я пытаюсь сгруппировать значения по желаемым глубинам, но не сумел это сделать - чаще, чем не доводить до плохого синтаксиса. Кроме того, если я не ошибаюсь, конвейер $ match вернет все глубины, пока погружение будет иметь значения на 1 и 4 метра, так что это не сработает.

С помощью инструмента find() я использую $ slice, чтобы возвращать значения, которые я намереваюсь использовать из массива, но не были успешными вместе с функцией aggregate().

Есть ли способ решить эту проблему? Спасибо заранее, очень ценим!

ответ

0

Вы должны были бы поместить ваш $match трубопровод перед тем $unwind к optimize вашей операции агрегации, как делают в $unwind операцию по всей коллекции потенциально может вызвать некоторые проблемы с производительностью, так как он создает копию каждый запись на запись в массив и использование большего объема памяти (возможная память на конвейере агрегации с общей суммой 10%), таким образом, занимает «время» для создания сплющенных массивов, а также «время» для его обработки. Следовательно, лучше ограничить количество документов, попадающих в трубопровод, чтобы сгладить.

db.collection.aggregate([ 
    { 
     "$match": { 
      "startdatetime": { 
       "$gt": new ISODate("2016-01-10T06:00:29Z"), 
       "$lt": new ISODate("2016-06-10T06:00:29Z") 
      }, 
      "diveValues.depth": { "$gte": 1, "$lte": 4 } 
     } 
    }, 
    { "$unwind": "$diveValues" }, 
    { "$match": { "diveValues.depth": { "$gte": 1, "$lte": 4 } } }, 
    { 
     "$group": { 
      "_id": { 
       "year": { "$year": "$startdatetime" }, 
       "month": { "$month": "$startdatetime" } 
      }, 
      "avgTemp": { "$avg": "$diveValues.temp" } 
     } 
    } 
]) 

Если вы хотите результаты содержат средние временные секретарь для всех глубин и для диапазона 1-4 глубины, то вам нужно будет запустить этот трубопровод, который будет использовать tenary оператор $cond кормить оператор $avg накопившиеся температуры в пределах группы, основанные на диапазоне глубин:

db.collection.aggregate([ 
    { 
     "$match": { 
      "startdatetime": { 
       "$gt": new ISODate("2016-01-10T06:00:29Z"), 
       "$lt": new ISODate("2016-06-10T06:00:29Z") 
      } 
     } 
    },  
    { "$unwind": "$diveValues" }, 
    { 
     "$group": { 
      "_id": { 
       "year": { "$year": "$startdatetime" }, 
       "month": { "$month": "$startdatetime" } 
      }, 
      "avgTemp": { "$avg": "$diveValues.temp" }, 
      "avgTempDepth1-4": { 
       "$avg": { 
        "$cond": [ 
         { 
          "$and": [ 
           { "$gte": [ "$diveValues.depth", 1 ] }, 
           { "$lte": [ "$diveValues.depth", 4 ] } 
          ] 
         }, 
         "$diveValues.temp", 
         null       
        ] 
       } 
      } 
     } 
    } 
]) 
+1

Большое спасибо, это сработало. И большое спасибо за отзыв о размещении матча перед тем, как расслабиться. – Vegar

+0

@Vegar Не беспокойтесь. Просто из любопытства, какой трубопровод вы использовали, первый или последний? – chridam

+0

Для моих нужд мне лучше всего подходит первый. Но я очень благодарен вам за то, что вы написали их обоим, потому что я нахожу его очень образованным. – Vegar

0

Прежде всего, дата $ матч оператор should be used at the beginning of the pipeline so that indexes can be used.

Теперь на вопрос, вам просто нужно фильтровать интервал глубины, как вы делали с датами:

db.col.aggregate([ 
    {"$match": { 
     'startdatetime': { 
      "$gt": new ISODate("2016-01-10T06:00:29Z"), 
      "$lt": new ISODate("2016-11-10T06:00:29Z") 
     } 
    }}, 
    {"$unwind": "$diveValues"}, 
    {"$match": { 
     "diveValues.depth": { 
      "$gte": 1.0, 
      "$lt": 4.0 
     } 
    }}, 
    {"$group": { 
     "_id": { 
      "year": {"$year": "$startdatetime" }, 
      "month": {"$month": "$startdatetime" } 
     }, 
     "avgTemp": { "$avg": "$diveValues.temp" }} 
    } 
]) 

Это даст вам в среднем только на выбранном интервале глубин.

+1

Спасибо большое, это выглядит, как он работает. Кроме того, спасибо за подсказку о размещении размотки. – Vegar

Смежные вопросы