2012-10-08 16 views
13

Jak elastyczna jest funkcja agregująca dla formatowania wyjściowego w MongoDB? Format

danych:

{ 
     "_id" : ObjectId("506ddd1900a47d802702a904"), 
     "port_name" : "CL1-A", 
     "metric" : "772.0", 
     "port_number" : "0", 
     "datetime" : ISODate("2012-10-03T14:03:00Z"), 
     "array_serial" : "12345" 
} 

Teraz używam tego agregatu funkcja zwraca tablicę DateTime, tablicy wskaźników, a count:

{$match : { 'array_serial' : array, 
          'port_name' : { $in : ports}, 
          'datetime' : { $gte : from, $lte : to} 
         } 
       }, 
       {$project : { port_name : 1, metric : 1, datetime: 1}}, 
       {$group : { _id : "$port_name", 
          datetime : { $push : "$datetime"}, 
          metric : { $push : "$metric"}, 
          count : { $sum : 1}}} 

Co jest miłe, i bardzo szybko, ale czy istnieje sposób na sformatowanie danych wyjściowych, więc istnieje jedna tablica na dane/dane? Tak:

[ 
    { 
     "_id" : "portname", 
     "data" : [ 
       ["2012-10-01T00:00:00.000Z", 1421.01], 
       ["2012-10-01T00:01:00.000Z", 1361.01], 
       ["2012-10-01T00:02:00.000Z", 1221.01] 
       ] 
    } 
] 

To znacznie uprości front-end jak to format kodu wykres spodziewa.

+0

Tymczasem dostaję wyjście i pętli przez obiekty i używając 'funkcję zip' podkreślenia, by je połączyć, to nie wydaje się, aby dodać wiele napowietrznych. –

Odpowiedz

16

Łączenie dwóch pól w tablicę wartości z Aggregation Framework jest możliwe, ale zdecydowanie nie jest tak proste, jak mogłoby być (przynajmniej na MongoDB 2.2.0).

Oto przykład:

db.metrics.aggregate(

    // Find matching documents first (can take advantage of index) 
    { $match : { 
     'array_serial' : array, 
     'port_name' : { $in : ports}, 
     'datetime' : { $gte : from, $lte : to} 
    }}, 

    // Project desired fields and add an extra $index for # of array elements 
    { $project: { 
     port_name: 1, 
     datetime: 1, 
     metric: 1, 
     index: { $const:[0,1] } 
    }}, 

    // Split into document stream based on $index 
    { $unwind: '$index' }, 

    // Re-group data using conditional to create array [$datetime, $metric] 
    { $group: { 
     _id: { id: '$_id', port_name: '$port_name' }, 
     data: { 
      $push: { $cond:[ {$eq:['$index', 0]}, '$datetime', '$metric'] } 
     }, 
    }}, 

    // Sort results 
    { $sort: { _id:1 } }, 

    // Final group by port_name with data array and count 
    { $group: { 
     _id: '$_id.port_name', 
     data: { $push: '$data' }, 
     count: { $sum: 1 } 
    }} 
) 
+0

Ah! Nie wiedziałem, że grupa $ może być wywołana więcej niż jeden raz, spróbuję, dzięki! –

+0

Dzięki za przybycie na ratunek, Stennie. :) –

+0

Co to jest "$ const" "zrób dokładnie? To nie wydaje się być udokumentowane. – maxdec

1

Budowanie tablic w strukturze agregacji bez $ push i $ addToSet to coś, czego wydaje się brakować. Próbowałem już wcześniej to zrobić i nie udało się. Byłoby świetnie, gdyby można po prostu zrobić:

data : {$push: [$datetime, $metric]} 

w $group, ale to nie działa.

także Budynek „dosłowne” obiekty takie jak ten nie działa:

data : {$push: {literal:[$datetime, $metric]}} 
or even data : {$push: {literal:$datetime}} 

Mam nadzieję, że w końcu wymyślić kilka lepszych sposobów masowania tego rodzaju danych.

+0

Oto dokładne metody, które próbowałem, po prostu zakładałem, że to zadziała. Chyba nie :( –

2

MongoDB 2,6 się to znacznie ułatwia wprowadzenie $map, co pozwala na formę prostszy Array transpozycji:

db.metrics.aggregate([ 
    { "$match": { 
     "array_serial": array, 
     "port_name": { "$in": ports}, 
     "datetime": { "$gte": from, "$lte": to } 
    }}, 
    { "$group": { 
     "_id": "$port_name", 
     "data": { 
      "$push": { 
       "$map": { 
        "input": [0,1], 
        "as": "index", 
        "in": { 
         "$cond": [ 
          { "$eq": [ "$$index", 0 ] }, 
          "$datetime", 
          "$metric" 
         ] 
        } 
       } 
      } 
     }, 
     "count": { "$sum": 1 } 
    }} 
]) 

przypadku bardzo podobne do podejścia z $unwind, dostarczasz tablicę jako "input" do operacji na mapie składającej się z dwóch wartości, a następnie w zasadzie zastępujesz te wartości wartościami pola, które chcesz uzyskać poprzezOperacja.

To faktycznie usuwa wszystkie żonglowanie potokami wymaganymi do przekształcenia dokumentu, tak jak było to wymagane w poprzednich wersjach, i po prostu pozostawia rzeczywistą agregację dla danego zadania, które jest w zasadzie gromadzone na wartość "nazwa_portu", a transformacja do tablicy jest nie jest już problemem.

0

można wykorzystywać $zip operatora w celu uzyskania wartości w tablicy 3.4

$zip z $arrayElemAt aby utworzyć szereg datetime i metrics.

Coś

db.collection.aggregate([ { 
    "$match": { 
     "array_serial": array, 
     "port_name": { 
     "$in": ports 
     }, 
     "datetime": { 
     "$gte": from, 
     "$lte": to 
     } 
    } 
    }, 
    { 
    "$group": { 
     "_id": "$port_name", 
     "data": { 
     "$push": { 
      "$arrayElemAt": [ 
      { 
       "$zip": { 
       "inputs": [ 
        [ 
        "$datetime" 
        ], 
        [ 
        "$metric" 
        ] 
       ] 
       } 
      }, 
      0 
      ] 
     } 
     }, 
     "count": { 
     "$sum": 1 
     } 
    } 
    } 
]) 
Powiązane problemy