9 agregacion y map reduce PDF

Title 9 agregacion y map reduce
Course Bases de Datos
Institution Universidad Politécnica de Madrid
Pages 46
File Size 875.2 KB
File Type PDF
Total Downloads 54
Total Views 145

Summary

Download 9 agregacion y map reduce PDF


Description

AGREGATION 1

FRAMEWORK Enrique Barra

QUE ES EL AGGREGATION FRAMEWORK 





Las agregaciones son operaciones que procesan entradas y devuelven resultados calculados. Estas operaciones agrupan valores de múltiples documentos juntos, y pueden realizar una gran variedad de operaciones sobre los datos para devolver un resultado simple. Tres tipos de agregación: 1.

The aggregation pipeline 

2.

The map-reduce function 

3.

  

https://docs.mongodb.org/manual/core/map-reduce/

The single purpose aggregation methods and commands 



https://docs.mongodb.org/manual/core/aggregation-pipeline/

https://docs.mongodb.org/manual/core/single-purpose-aggregation/

Comparativa de los 3 tipos de agregación: https://docs.mongodb.org/manual/reference/aggregation-commandscomparison/ Más info: https://docs.mongodb.org/manual/aggregation/ Ejemplos: https://docs.mongodb.org/v3.0/applications/aggregation/ Video: https://www.youtube.com/watch?v=9HJxi7Q7YJA 2

AGGREGATION PIPELINE 3

AGGREGATION PIPELINE 

El framework de agregación está diseñado siguiendo el concepto del procesado de datos con pipelines (tuberías)



Los documentos entran en un pipeline que los va transformando en un resultado agregado Se dice que funciona en fases o “stages”



Cada fase es:



Filtro que operan como queries  Transformación que modifican la forma de salida del documento  Agrupación por campos  Ordenación por campos 



Funciona en particiones



Puede usar índices en alguna fase para mejorar la eficiencia 4

db.collection.aggregate(pipeline, options) 

Pipeline: es un array con una secuencia de operaciones o stages (fases)



Options: opciones extra: explain, allowDiskUse, cursor, bypassDocumentValidation, readConcern



Pipeline operators:



$match: Filtrar documentos $project: como projection en el find, obtener sólo lo que queremos de cada documento $group: agrupar documentos $unwind: expandir documentos $sort: ordenar



$limit/$skip: paginar documentos



$redact: restringir documentos



$geoNear: ordenar por proximidad



$lookup: left outer join con otra colección de la misma bbdd (nuevo en v3.2)



$out: escribir los resultados a una colección

   

5

ACUMULADORES DE LA FASE $GROUP  

 



https://docs.mongodb.org/v3.0/reference/operator/aggregation-group La fase $group calcula valores procesando documentos que comparten una clave

Pasa a la fase siguiente un documento por cada agrupación que realiza El documento de salida tiene un campo _id que contiene la clave que se ha usado para la agrupación y puede contener campos calculados con valores de algún acumulador Acumuladores:   

    

$sum: suma $avg: media $first: primer documento $last: último $max: máximo $min: mínimo $push: devuelve un array con todos los valores procesados por una expresión $addToSet: devuelve un array con todos los valores procesados por una expresión y únicos 6

AGGREGATION PIPELINE

QUEREMOS EL PRECIO TOTAL POR CLIENTE CON STATUS “A”

https://docs.mongodb.org/manual/_images/aggregation-pipeline.png

7

FRAMEWORK DE AGREGACIÓN – EJEMPLO SIMPLE { "_id" : ObjectId("50b1aa983b3d0043b51b2c52"), "name" : "Nexus 7", "category" : "Tablets", "manufacturer" : "Google", "price" : 199 } 

Query que suma todos los productos de cada categoría:

db.products.aggregate([ { $group: { _id: "$category", num_products: {$sum:1} } } ])

8

JSON QUE USAREMOS PARA OTRO EJEMPLO { "_id": "10280", "city": "NEW YORK", "state": "NY", “borough": “Park Avenue", "pop": 5574, "loc": [

-74.016323, 40.710537 ] } 

Queremos los estados con una población superior a 10 millones



Queremos la población media por estado



Queremos las ciudades más grandes y más pequeñas por estado

9

ESTADOS CON UNA POBLACIÓN SUPERIOR A 10 MILLONES db.zipcodes.aggregate([ { $group: { _id: "$state", totalPop: { $sum: "$pop" } } }, { $match: { totalPop: { $gte: 10 * 1000 * 1000 } } }

])  

 

 

Esta operación está formada por una fase $group y una fase $match. $group agrupa los documentos por el campo “state”, y calcula para cada estado la población total. Devuelve un documento para cada estado. El nuevo documento tiene dos campos, _id y totalPop $match toma estos documentos y devuelve sólo los que tienen más de 10M de totalPop Equivalencia en SQL: SELECT state, SUM(pop) AS totalPop FROM zipcodes GROUP BY state HAVING totalPop >= (10*1000*1000)

10

POBLACIÓN MEDIA POR ESTADO db.zipcodes.aggregate([ {$group: {_id: {state: "$state", city: "$city"}, pop: {$sum: "$pop"}}}, {$group: { _id: "$_id.state", avgCityPop: { $avg: "$pop" }}}

])   

2 fases group La primera agrupa los documentos por ciudad y estado y calcula la población total para cada uno Tras este paso los documentos son así:

{ "_id" : {

"state" : "CO", "city" : "EDGEWATER" }, "pop" : 13154 }

11

POBLACIÓN MEDIA POR ESTADO 

La segunda fase $group agrupa los documentos en el pipeline por el _id.state (campo state en el documento _id), y usa $avg para calcular el valor avgCityPop, sacando un documento para cada estado

{ "_id" : "MN", "avgCityPop" : 5335 }

12

CIUDADES MÁS GRANDES Y MAS PEQUEÑAS POR ESTADO db.zipcodes.aggregate([ { $group: { _id: { state: "$state", city: "$city" }, pop: { $sum: "$pop" } } }, { $sort: { pop: 1 } }, { $group: { _id: "$_id.state",

biggestCity: { $last: "$_id.city" }, biggestPop: { $last: "$pop" }, smallestCity: { $first: "$_id.city" }, smallestPop: { $first: "$pop" } } }, { $project: { _id: 0, state: "$_id", biggestCity: { name: "$biggestCity", pop: "$biggestPop" }, smallestCity: { name: "$smallestCity", pop: "$smallestPop" } } } ]) 13

MAP-REDUCE 14

EXPLICACIÓN MAP-REDUCE 







La función map se encarga del mapeo o asociación y es aplicada en paralelo para cada ítem en la entrada de datos. Toma uno de los pares de datos con un tipo en un dominio de datos, y devuelve una lista de pares en un dominio diferente: Map(k1,v1) -> list(k2,v2) Importante -> map() produce una lista de pares (k2,v2) por cada llamada

Después de eso, el framework de MAPREDUCE junta todos los pares con la misma clave de todas las listas y los agrupa, creando un grupo por cada una de las diferentes claves generadas (como la fase $group) Finalmente la función reduce es aplicada en paralelo para cada grupo, produciendo una colección de valores para cada dominio Reduce(k2, list (v2)) -> list(v3)



Por lo tanto MapReduce transforma una lista de pares (clave, valor) en una lista de valores 15

MAP-REDUCE EN MONGODB 







Las operaciones map-reduce utilizan funciones JavaScript y tienen dos fases (y una extra opcional):  Map: procesa cada documento y emite uno o más objetos (key, value) para cada documento de entrada  Reduce: combina la salida del map  Opcionalmente puede haber una tercera fase “finalize” que hace unas modificaciones finales

Al usar funciones JavaScript son más potentes que el aggregation pipeline pero en general es menos eficiente Funciona en particiones

Ejemplos: https://docs.mongodb.org/manual/tutorial/map-reduceexamples/ 16

JSON CON EL QUE VAMOS A HACER LOS EJEMPLOS { _id: ObjectId("50a8240b927d5d8b5891743c"), cust_id: "abc123", ord_date: new Date("Oct 04, 2012"), status: 'A', price: 25, items: [ { sku: "mmm", qty: 5, price: 2.5 }, { sku: "nnn", qty: 5, price: 2.5 } ] }

Stock-keeping unit o SKU (en castellano número de referencia) es un identificador usado en el comercio con el objeto de permitir el seguimiento sistémico de los productos y servicios ofrecidos a los clientes. Cada SKU se asocia con un objeto, producto, marca, servicio, cargos, etc. 17

MAP REDUCE – EXPLICACIÓN VISUAL

QUEREMOS EL PRECIO TOTAL POR CLIENTE CON STATUS “A”

https://docs.mongodb.org/manual/_images/map-reduce.png

18

MISMO MAP-REDUCE

CON LAS FUNCIONES DEFINIDAS SEPARADAS 

“this” se refiere al documento que se está procesando en el map en ese momento

var mapFunction1 = function () { emit(this.cust_id, this.amount); };

var reduceFunction1 = function (key, values) { return Array.sum(values); }; db.orders.mapReduce( mapFunction1, reduceFunction1, { query: { “status”: “A”}, out: “order_totals" }

) 19

OTRO EJEMPLO 

Para las órdenes posteriores a 01/01/2012 calcular el número de órdenes y cantidad total para cada “sku”.



Calcular también cantidad media por orden por cada valor “sku”



Recordemos:

{

_id: ObjectId("50a8240b927d5d8b5891743c"), cust_id: "abc123", ord_date: new Date("Oct 04, 2012"), status: 'A',

price: 25, items: [ { sku: "mmm", qty: 5, price: 2.5 }, { sku: "nnn", qty: 5, price: 2.5 } ] }

20

MAP 



Para cada documento, esta función recorre el array “items” y asocia el sku con un nuevo valor (un objeto) que contiene “count” 1 y la cantidad (qty) de la orden Y emite varios pares (sku, valor), uno por item

var mapFunction2 = function () { for (var idx = 0; idx < this.items.length; idx++) { var key = this.items[idx].sku; var value = { count: 1, qty: this.items[idx].qty }; emit(key, value); } }; 21

REDUCE 

 



Recuce recibe como siempre los pares “key,value” que emite el map, ya agregados/filtrados por key (eso lo hace Mongo) Key es el “sku” Value es “countObjVals”: que es un array cuyos elementos son los objetos {count: 1, qty: XXX} que emitió el map Esta función recude los arrays “countObjVals” a un sólo objeto “reducedVal” que contiene la suma de los campos “qty” y la suma de los campos “count”

var reduceFunction2 = function (keySKU, countObjVals) { reducedVal = { count: 0, qty: 0 }; for (var idx = 0; idx < countObjVals.length; idx++) { reducedVal.count += countObjVals[idx].count; reducedVal.qty += countObjVals[idx].qty; } return reducedVal; }; 22

FINALIZE 

La función finalize recibe dos argumentos, “key, reducedVal”. Esta función modifica el reducedVal y añade un tercer campo llamado “avg” con la media y devuelve el objeto modificado.

var finalizeFunction2 = function (key, reducedVal) { reducedVal.avg = reducedVal.qty / reducedVal.count; return reducedVal; };

23

MAP-REDUCE  

“Query” es para realizar el map-reduce sólo sobre la fecha que se quiere “Out” dice que se cree una colección “map_reduce_example”, y que si ya existe se haga merge (mezcla) de los contenidos

db.orders.mapReduce( mapFunction2, reduceFunction2, { out: { merge: "map_reduce_example" }, query: { ord_date: { $gt: new Date('01/01/2012') } }, finalize: finalizeFunction2 } ) 24

MAP-REDUCE INCREMENTAL I 

Si nuestra colección está creciendo y queremos hacer un map-reduce continuo, no tenemos que hacerlo con la colección completa cada vez. Se puede hacer incremental



Será clave para el map-reduce incremental tener un timestamp (ts).



Lo haremos en dos pasos.



Consideremos la siguiente colección, donde length es el tiempo de sesión en segundos.    

   



db.sessions.save({ db.sessions.save({ db.sessions.save({ db.sessions.save({

userid: userid: userid: userid:

"a", "b", "c", "d",

ts: ts: ts: ts:

ISODate('2011-11-03 ISODate('2011-11-03 ISODate('2011-11-03 ISODate('2011-11-03

14:17:00'), 14:23:00'), 15:02:00'), 16:45:00'),

length: length: length: length:

95 }); 110 }); 120 }); 45 });

db.sessions.save({ db.sessions.save({ db.sessions.save({ db.sessions.save({

userid: userid: userid: userid:

"a", "b", "c", "d",

ts: ts: ts: ts:

ISODate('2011-11-04 ISODate('2011-11-04 ISODate('2011-11-04 ISODate('2011-11-04

11:05:00'), 13:14:00'), 17:00:00'), 15:37:00'),

length: length: length: length:

105 }); 120 }); 130 }); 65 });

Queremos calcular el tiempo total que ha pasado cada usuario y el tiempo medio por sesión del usuario 25

MAP-REDUCE INCREMENTAL I 

Como map definimos una función que mapea el userid a un objeto que contiene el userid, total_time, count y avg

var mapFunction = function () { var key = this.userid; var value = { userid: this.userid, total_time: this.length, count: 1, avg_time: 0 }; emit(key, value); };

26

MAP-REDUCE INCREMENTAL I 

Como reduce definimos una función que recibe key y value y calcula el tiempo total y la cuenta total

var reduceFunction = function (key, values) { var reducedObject = { userid: key, total_time: 0, count: 0, avg_time: 0 }; values.forEach(function (value) { reducedObject.total_time += value.total_time; reducedObject.count += value.count; } ); return reducedObject;

}; 27

MAP-REDUCE INCREMENTAL I 

Como finalize definimos una función que recibe key y reducedvalue y calcula además el average

var finalizeFunction = function (key, reducedValue) { if (reducedValue.count > 0)

reducedValue.avg_time = reducedValue.total_time / reducedValue.count; return reducedValue; };

28

MAP-REDUCE INCREMENTAL I 

Hacemos el map-reduce con las tres funciones y la salida la mandamos a una colección session_stat. Si la colección existe la reemplaza

db.sessions.mapReduce(mapFunction, reduceFunction, {

out: "session_stat", finalize: finalizeFunction } )

29

MAP-REDUCE INCREMENTAL I 

Ahora la colección va creciendo. Supongamos que metemos estos datos:

db.sessions.save({ userid: "a", ts: ISODate('2011-11-05 14:17:00'), length: 100 });

db.sessions.save({ userid: "b", ts: ISODate('2011-11-05 14:23:00'), length: 115 }); db.sessions.save({ userid: "c", ts: ISODate('2011-11-05 15:02:00'), length: 125 }); db.sessions.save({ userid: "d", ts: ISODate('2011-11-05 16:45:00'), length: 55 });



Podremos hacer el siguiente map-reduce incremental así. Fijarse en el operador “reduce” que se le pasa a “out”. Hará que se reduzca no sólo los nuevos datos sino también los ya existentes en la colección session_stat

db.sessions.mapReduce(mapFunction,

reduceFunction, { query: { ts: { $gt: ISODate('2011-11-05 00:00:00') } }, out: { reduce: "session_stat" }, finalize: finalizeFunction }

); 30

SINGLE PURPOSE AGGREGATION OPERATIONS 31

SINGLE PURPOSE AGGREGATION OPERATIONS 

Mongo también provee los comandos db.collection.count(), db.collection.group(),db.collection.distinct()



Todos estas operaciones agregan documentos de una colección, pero son menos flexibles que las otras opciones de agregación No funciona en particiones



count(query, options):



Devuelve el número de documentos que encajan con la query.  Ejemplo: 





db.orders.count( { ord_dt: { $gt: new Date('01/01/2012') } } )

distinct(field, query): Encuentra los valores diferentes de un campo específico en una colección y devuelve los resultados en un array  db.inventory.distinct( "dept" ) 

32



group({ key, reduce, initial [, keyf] [, cond] [, finalize] }): Agrupa documentos en una colección por la “key” y hace agregaciones simples. Equivalente a SELECT GROUP BY de SQL.  $group del aggregation pipeline hace lo mismo pero es más potente. 



Ejemplo:

db.orders.group( { key: { ord_dt: 1, 'item.sku': 1 }, cond: { ord_dt: { $gt: new Date('01/01/2012') } }, reduce: function (curr, result) { result.total += curr.item.qty; }, initial: { total: 0 } } )

33

AUTOEVALUACIÓN 34

Given the following collection: > db.stuff.find() 

{ "_id" : ObjectId("50b26f9d80a78af03b5163c8"), "a" : 1, "b" : 1, "c" : 1 } { "_id" : ObjectId("50b26fb480a78af03b5163c9"), "a" : 2, "b" : 2, "c" : 1 } { "_id" : ObjectId("50b26fbf80a78af03b5163ca"), "a" : 3, "b" : 3, "c" : 1 } { "_id" : ObjectId("50b26fcd80a78af03b5163cb"), "a" : 3, "b" : 3, "c" : 2 } { "_id" : ObjectId("50b26fd380a78af03b5163cc"), "a" : 3, "b" : 5, "c" : 3 } { "_id" : ObjectId("50b27f7080a78af03b5163cd"), "a" : 3, "b" : 3, "c" : 2 }



And the following aggregation query: db.stuff.aggregate([{$group: {_id: {'moe':'$a', 'larry':'$b', 'curly':'$c' } } }])



How many documents will be in the result set?     

2 3 4 5 6

35

Given the following collection: > db.stuff.find() 

{ "_id" : ObjectId("50b26f9d80a78af03b5163c8"), "a" : 1, "b" : 1, "c" : 1 } { "_id" : ObjectId("50b26fb480a78af03b5163c9"), "a" : 2, "b" : 2, "c" : 1 } { "_id" : ObjectId("50b26fbf80a78af03b5163ca"), "a" : 3, "b" : 3, "c" : 1 } { "_id" : ObjectId("50b26fcd80a78af03b5163cb"), "a" : 3, "b" : 3, "c" : 2 } { "_id" : ObjectId("50b26fd380a78af03b5163cc"), "a" : 3, "b" : 5, "c" : 3 } { "_id" : ObjectId("50b27f7080a78af03b5163cd"), "a" : 3, "b" : 3, "c" : 2 }



And the following aggregation query: db.stuff.aggregate([{$group: {_id: {'moe':'$a', 'larry':'$b', 'curly':'$c' } } }])

     

How many documents will be in the result set? 2 3 4 5 // toma todos los documentos que tienen la combinación a,b,c diferentes 6 36

Given the following collection: > db.stuff.find() 

{ "_id" { "_id" { "_id" { "_id" { "_id" { "_id"



: ObjectId("50b26f9d80a78af03b5163c8"), "a" : 1, "b" : 1, "c" : 1 } : ObjectId("50b26fb480a78af03b5163c9"), "a" : 2, "b" : 2, "c" : 1 } : ObjectId("50b26fbf80a78af03b5163ca"), "a" : 3, "b" : 3, "c" : 1 } : ObjectId("50b26fcd80a78af03b5163cb"), "a" : 3, "b" : 3, "c" : 2 } : ObjectId("50b26fd380a78af03b5163cc"), "a" : 3, "b" : 5, "c" : 3 } : ObjectId("50b27f7080a78af03b5163cd"), "a" : 3, "b" : 3, "c" : 2 }

And the following aggregation query: db.stuff.aggregate([{$group:{_id:'$c'}}])



How many documents will be in the result set?     

2 3 4 5 6

37

Given the following collection: > db.stuff.find() 

{ "_id" { "_id" { "_id" { "_id" { "_id" { "_id"



: ObjectId("50b26f9d80a78af03b5163c8"), "a" : 1, "b" : 1, "c" : 1 } : ObjectId("50b26fb480a78af03b5163c9"), "a" : 2, "b" : 2, "c" : 1 } : ObjectId("50b26fbf80a78af03b5163ca"), "a" : 3, "b" : 3, "c" : 1 } : ObjectId("50b26fcd80a78af03b5163cb"), "a" : 3, "b" : 3, "c" : 2 } : ObjectId("50b26fd380a78af03b5163cc"), "a" : 3, "b" : 5, "c" : 3 } : ObjectId("50b27f7080a78af03b5163cd"), "a" : 3, "b" : 3, "c" : 2 }
...


Similar Free PDFs