Mam następującą strukturę danych i chciałby osadzić dokumenty zawierające pole LOC do dokumentów zawierających czas trwania, ale tylko wtedy, gdy znacznik czasu znajduje się w znaczniku czasu (TS) minus czas trwania w sekundach dokumentu nadrzędnego. Czy to możliwe dzięki ramowi agregacji lub z mapą zmniejsza się?
{
"_id" : ObjectId("53df2a44e6583c76253c9869"),
"deviceId" : NumberLong(1377700226807),
"ts" : ISODate("2014-08-04T08:37:55.000Z"),
"duration" : NumberLong(1642),
}
{
"_id" : ObjectId("53df2a41e6583c4e243c9869"),
"deviceId" : NumberLong(1377700226807),
"ts" : ISODate("2014-08-04T08:37:53.000Z"),
"loc" : {
"lon" : 5.1101453,
"lat" : 52.0625047
}
}
{
"_id" : ObjectId("53df2a3fe6583c38203c986a"),
"deviceId" : NumberLong(1377700226807),
"ts" : ISODate("2014-08-04T08:37:50.000Z"),
"loc" : {
"lon" : 5.1101297,
"lat" : 52.0625031
}
}
{
"_id" : ObjectId("53df2a44e6583c76253c9869"),
"deviceId" : NumberLong(1377700226807),
"ts" : ISODate("2014-08-04T06:37:55.000Z"),
"duration" : NumberLong(3600),
}
{
"_id" : ObjectId("53df2a38e6583c03253c9869"),
"deviceId" : NumberLong(1377700226807),
"ts" : ISODate("2014-08-04T06:37:44.000Z"),
"loc" : {
"lon" : 5.1101176,
"lat" : 52.0625171
}
}
{
"_id" : ObjectId("53df2a33e6583c51243c9869"),
"deviceId" : NumberLong(1377700226807),
"ts" : ISODate("2014-08-04T06:37:38.000Z"),
"loc" : {
"lon" : 5.1101409,
"lat" : 52.0625818
}
}
{
"_id" : ObjectId("53df2a2de6583c38203c9869"),
"deviceId" : NumberLong(1377700226807),
"ts" : ISODate("2014-08-04T06:37:32.000Z"),
"loc" : {
"lon" : 5.1099513,
"lat" : 52.0624157
}
}
Jest to pożądany format
{
"_id" : ObjectId("53df2a44e6583c76253c9869"),
"deviceId" : NumberLong(1377700226807),
"ts" : ISODate("2014-08-04T08:37:55.000Z"),
"duration" : NumberLong(1642),
"data" : [
{
"ts" : ISODate("2014-08-04T08:37:53.000Z"),
"loc" : {
"lon" : 5.1101453,
"lat" : 52.0625047
}
},
{
"ts" : ISODate("2014-08-04T08:37:50.000Z"),
"loc" : {
"lon" : 5.1101297,
"lat" : 52.0625031
}
}
]
}
2 odpowiedzi
Nie można to łatwo zrobić z ramami agregacji, ale można to zrobić za pomocą MapReduce.
Zakładając, że dane są zebrane nieruchomości (tj. Nie ma brakujących dokumentów "jazdy" z czasem trwania dokumentów z wartościami "LOC") Możesz to zrobić w ten sposób:
map=function () {
var startTime;
if (this.hasOwnProperty("duration"))
startTime=this.ts-this.duration*1000;
else
startTime=this.ts;
emit(this.deviceId, {startTs:new Date(startTime), endTs:this.ts, loc:this.loc, duration:this.duration});
}
Mapa Wyjścia rzeczy w znormalizowanym formacie, zmniejsz grupy je wszystkie do pojedynczej tablicy na urządzenie serwowane.
reduce=function (key,values) {
var result = { vals : [ ] };
values.forEach(function(v) {
result.vals.push(v);
})
return result;
}
Wszystkie rzeczywiste przetwarzanie (grupowanie dla każdego urządzenia urządzenia) dzieje się w funkcji finalize
, która otrzymuje tablicę dla każdego deviceId
, które sortuje, a grupy do dokumentu, którego oczekujesz.
finalize=function (key, value) {
var lastI=-1;
var result = {rides: [ ] };
var ride = { };
value.vals.sort(function(a,b) { return a.startTs.getTime() - b.startTs.getTime(); } );
for (i=0; i<value.vals.length; i++) {
if (value.vals[i].loc == null ) {
if (ride.hasOwnProperty("locations")) {
result.rides.push(ride);
ride={};
}
ride["start"]=value.vals[i].startTs;
ride["end"]=value.vals[i].endTs;
ride["duration"]=value.vals[i].duration;
ride["locations"]=[];
lastI=i;
} else {
ride.locations.push({ loc: value.vals[i].loc, ts: value.vals[i].endTs});
}
}
result.rides.push(ride);
return result;
}
Dodałem kilka urządzeń urządzeń do danych testowych:
db.rides.find({},{_id:0})
{ "deviceId" : NumberLong("1377700226807"), "ts" : ISODate("2014-08-04T06:37:32Z"), "loc" : { "lon" : 5.1099513, "lat" : 52.0624157 } }
{ "deviceId" : NumberLong("1377700226910"), "ts" : ISODate("2014-08-04T06:37:32Z"), "loc" : { "lon" : 5.1099513, "lat" : 52.0624157 } }
{ "deviceId" : NumberLong("1377700226807"), "ts" : ISODate("2014-08-04T06:37:38Z"), "loc" : { "lon" : 5.1101409, "lat" : 52.0625818 } }
{ "deviceId" : NumberLong("1377700226910"), "ts" : ISODate("2014-08-04T06:37:38Z"), "loc" : { "lon" : 5.1101409, "lat" : 52.0625818 } }
{ "deviceId" : NumberLong("1377700226807"), "ts" : ISODate("2014-08-04T06:37:44Z"), "loc" : { "lon" : 5.1101176, "lat" : 52.0625171 } }
{ "deviceId" : NumberLong("1377700226910"), "ts" : ISODate("2014-08-04T06:37:44Z"), "loc" : { "lon" : 5.1101176, "lat" : 52.0625171 } }
{ "deviceId" : NumberLong("1377700226807"), "ts" : ISODate("2014-08-04T06:37:55Z"), "duration" : NumberLong(3600) }
{ "deviceId" : NumberLong("1377700226910"), "ts" : ISODate("2014-08-04T06:37:55Z"), "duration" : NumberLong(3600) }
{ "deviceId" : NumberLong("1377700226807"), "ts" : ISODate("2014-08-04T08:37:50Z"), "loc" : { "lon" : 5.1101297, "lat" : 52.0625031 } }
{ "deviceId" : NumberLong("1377700226908"), "ts" : ISODate("2014-08-04T08:37:50Z"), "loc" : { "lon" : 5.1101297, "lat" : 52.0625031 } }
{ "deviceId" : NumberLong("1377700226807"), "ts" : ISODate("2014-08-04T08:37:53Z"), "loc" : { "lon" : 5.1101453, "lat" : 52.0625047 } }
{ "deviceId" : NumberLong("1377700226908"), "ts" : ISODate("2014-08-04T08:37:53Z"), "loc" : { "lon" : 5.1101453, "lat" : 52.0625047 } }
{ "deviceId" : NumberLong("1377700226807"), "ts" : ISODate("2014-08-04T08:37:55Z"), "duration" : NumberLong(1642) }
{ "deviceId" : NumberLong("1377700226908"), "ts" : ISODate("2014-08-04T08:37:55Z"), "duration" : NumberLong(1642) }
I prowadził go przez pana
db.rides.mapReduce(map, reduce, {out:"newrides", finalize:finalize})
{
"result" : "frides",
"timeMillis" : 47,
"counts" : {
"input" : 14,
"emit" : 14,
"reduce" : 3,
"output" : 3
},
"ok" : 1
}
Wyniki to:
db.newrides.find().pretty()
{
"_id" : NumberLong("1377700226807"),
"value" : {
"rides" : [
{
"start" : ISODate("2014-08-04T05:37:55Z"),
"end" : ISODate("2014-08-04T06:37:55Z"),
"duration" : NumberLong(3600),
"locations" : [
{
"loc" : {
"lon" : 5.1099513,
"lat" : 52.0624157
},
"ts" : ISODate("2014-08-04T06:37:32Z")
},
{
"loc" : {
"lon" : 5.1101409,
"lat" : 52.0625818
},
"ts" : ISODate("2014-08-04T06:37:38Z")
},
{
"loc" : {
"lon" : 5.1101176,
"lat" : 52.0625171
},
"ts" : ISODate("2014-08-04T06:37:44Z")
}
]
},
{
"start" : ISODate("2014-08-04T08:10:33Z"),
"end" : ISODate("2014-08-04T08:37:55Z"),
"duration" : NumberLong(1642),
"locations" : [
{
"loc" : {
"lon" : 5.1101297,
"lat" : 52.0625031
},
"ts" : ISODate("2014-08-04T08:37:50Z")
},
{
"loc" : {
"lon" : 5.1101453,
"lat" : 52.0625047
},
"ts" : ISODate("2014-08-04T08:37:53Z")
}
]
}
]
}
}
{
"_id" : NumberLong("1377700226908"),
"value" : {
"rides" : [
{
"start" : ISODate("2014-08-04T08:10:33Z"),
"end" : ISODate("2014-08-04T08:37:55Z"),
"duration" : NumberLong(1642),
"locations" : [
{
"loc" : {
"lon" : 5.1101297,
"lat" : 52.0625031
},
"ts" : ISODate("2014-08-04T08:37:50Z")
},
{
"loc" : {
"lon" : 5.1101453,
"lat" : 52.0625047
},
"ts" : ISODate("2014-08-04T08:37:53Z")
}
]
}
]
}
}
{
"_id" : NumberLong("1377700226910"),
"value" : {
"rides" : [
{
"start" : ISODate("2014-08-04T05:37:55Z"),
"end" : ISODate("2014-08-04T06:37:55Z"),
"duration" : NumberLong(3600),
"locations" : [
{
"loc" : {
"lon" : 5.1099513,
"lat" : 52.0624157
},
"ts" : ISODate("2014-08-04T06:37:32Z")
},
{
"loc" : {
"lon" : 5.1101409,
"lat" : 52.0625818
},
"ts" : ISODate("2014-08-04T06:37:38Z")
},
{
"loc" : {
"lon" : 5.1101176,
"lat" : 52.0625171
},
"ts" : ISODate("2014-08-04T06:37:44Z")
}
]
}
]
}
}
Ramy agregacji nie ma sposobu "zachowania" informacji w dokumentach podczas przetwarzania rurociągu. Rodzaj relacji "rodziców / dziecka" Opisujesz, gdzie element ten jest podejmowany w porównaniu z dokumentem "rodziców", który nie jest inaczej określony bezpośrednio w dokumentach, nie jest zatem możliwa tutaj.
Metoda MapReduce ma jednak dostęp do "globalnych" zmiennych zręgowanych. Może to zezwolić na "idenification" danych nadrzędnych, które mogą być następnie przechowywane w zmiennej do porównania do możliwych dzieci, ile potrzebujesz.
db.collection.mapReduce(
function() {
lastValue.data = [];
if ( lastId == null || this.hasOwnProperty("duration") ) {
lastId = this._id;
lastValue.deviceId = this.deviceId;
lastValue.ts = this.ts;
lastValue.duration = this.duration;
}
if (
( this.hasOwnProperty("loc") ) &&
(
( lastValue.ts.valueOf() - ( lastValue.duration * 1000 ) ) <
this.ts.valueOf()
)
)
lastValue.data.push({
"ts": this.ts,
"loc": this.loc
});
emit ( lastId, lastValue );
},
function (key,values) {
var reduced = {};
values.forEach(function(value) {
if ( !reduced.hasOwnProperty("deviceId") ) {
reduced.deviceId = value.deviceId;
reduced.ts = value.ts;
reduced.duration = value.duration;
reduced.data = [];
}
value.data.forEach(function(dat) {
reduced.data.push(dat);
});
});
return reduced;
},
{
"sort": { "ts": -1 },
"scope": { "lastId": null, "lastValue": {} },
"out": { "inline": 1 }
}
)
Zasadniczo jest to "przechowywanie" "klucz", który ma być emitowany jako "listed" z wykorzystaniem deklaracji "zakres" dostępnych do MapRed. Wyczyść dokumenty "Rodziców" zawierają czas trwania, więc jest coś do użycia.
Element "Data" musi być emitowany jako tablica. Wynika to z Wymóg "Zmniejsz "Funkcja zgodnie z opisem w dokumentacji. Key będąc tym, że wejście musi znajdować się w tym samym formacie, co oczekiwane wejście. Funkcja "Zmniejszyć" można nazwać więcej niż raz na "klucz".
Stan w "Mapper" może ograniczyć wartości obecne w tej tablicy przed wywoływaniem funkcji "Zmniejsz". Pozwala to również uniknąć opierając się na dodaniu metody "finalize" w przypadku, gdy jest tylko pojedynczy "kluczowy", a funkcja "Zmniejsz" nie dotknąłby tego elementu. Więc mniej pracy, aby wykonać filtrowanie front.
Następnie "reduktor" jest obecnie zdegradowany, aby po prostu "łączyć" wyniki, aby połączyć elementy w "danych", które kwalifikowały się do włączenia jako "dzieci".
Oczywiście porządek sortowania jest "malejącym" znacznik czasu lub wartości "TS". Byłby to dobry punkt do indeksu, a zamówienie wyrównuje się z sposobem przetwarzania dokumentów do porównania, aby każdy "przełamanie" jest ważny, gdy wykryto nowy rodzic.
Wyjście jest oczywiście w stylu mapReduce. Więc dotarcie do twojego "dokładnego" pożądanego wyjścia wymagałoby trochę więcej przetwarzania postu, ale jak widać, jest to zasadniczo wynik:
"results" : [
{
"_id" : ObjectId("53f15b8beb75bdce84f914a1"),
"value" : {
"deviceId" : NumberLong("1377700226807"),
"ts" : ISODate("2014-08-04T08:37:55Z"),
"duration" : NumberLong(1642),
"data" : [
{
"ts" : ISODate("2014-08-04T08:37:53Z"),
"loc" : {
"lon" : 5.1101453,
"lat" : 52.0625047
}
},
{
"ts" : ISODate("2014-08-04T08:37:50Z"),
"loc" : {
"lon" : 5.1101297,
"lat" : 52.0625031
}
}
]
}
},
{
"_id" : ObjectId("53f15b8beb75bdce84f914a4"),
"value" : {
"deviceId" : NumberLong("1377700226807"),
"ts" : ISODate("2014-08-04T06:37:55Z"),
"duration" : NumberLong(3600),
"data" : [
{
"ts" : ISODate("2014-08-04T06:37:44Z"),
"loc" : {
"lon" : 5.1101176,
"lat" : 52.0625171
}
},
{
"ts" : ISODate("2014-08-04T06:37:38Z"),
"loc" : {
"lon" : 5.1101409,
"lat" : 52.0625818
},
{
"ts" : ISODate("2014-08-04T06:37:32Z"),
"loc" : {
"lon" : 5.1099513,
"lat" : 52.0624157
}
}
]
}
}
]
Należy pamiętać, że wyjście podane tutaj jako różne wartości {x0}} niż próbka, ponieważ próbka zawiera wartości "duplikaty" dla _id
, co oczywiście nie jest dozwolone w kluczu podstawowym.