Primeros Pasos en MongoDB

Leganés 6-7 Febrero 2014 Víctor Cuervo Palencia Primeros Pasos en MongoDB Except where otherwise noted, this work is licensed under: http://creativ

2 downloads 215 Views 2MB Size

Story Transcript

Leganés 6-7 Febrero 2014

Víctor Cuervo Palencia

Primeros Pasos en MongoDB

Except where otherwise noted, this work is licensed under: http://creativecommons.org/licenses/by-nc-sa/3.0/

c_b_n_a

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Organizándonos…

1. Sobre el Ponente 2. ¿Qué es eso del NOSQL? 3. Hablemos de MongoDB 4. Pero haz ya una SELECT 5. ¿Tercera forma normal? 6. Rendimiento… Indexame 7. Agregar datos en MongoDB 8. Java, Python, Javascript,… 9. Más, más, más… 10. Herramientas 11. Quiero aprender más

¿Y nos vas a contar todo esto en 30m????

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Sobre el ponente: Víctor Cuervo

Responsable del Área QA Arquitecto Técnico ISBAN. Grupo Santander

Programador, Arquitecto IT, álter ego de Línea de Código, amante de las tecnologías, generador de conocimiento y facilitador del aprendizaje. http://lineadecodigo.com http://www.victorcuervo.com

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

¿Qué es eso del NOSQL?

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

WITH DATOS (A, B) AS( SELECT 'CREATE TABLE ' || RTRIM(TENTNOM) || ' (', 0 AS TATAORD FROM DEADIC.ENTIDAD_16 WHERE TENTCLA=&CODIGO_ENTIDAD AND ENTOR='DE' UNION ALL SELECT RTRIM(TATANOM) ||' '|| CASE WHEN TATAFOR ='CHR' THEN 'CHARACTER' WHEN TATAFOR ='DAT' THEN 'DATE' WHEN TATAFOR ='DEC' THEN 'DECIMAL' WHEN TATAFOR ='TST' THEN 'TIMESTAMP' WHEN TATAFOR ='VCH' THEN 'VARCHAR' WHEN TATAFOR ='INT' THEN 'INTEGER' WHEN TATAFOR ='FLO' THEN 'FLOAT' END

|| CASE WHEN TATAFOR ='DEC' THEN ' ( '|| RTRIM(CHAR(TATALON)) ||','|| SUBSTR(RTRIM(CHAR(TATADEC)), 1 , 3)||' ), ' WHEN TATAFOR ='CHR' THEN ' ( '|| RTRIM(CHAR(TATALON))||' ), ' WHEN TATAFOR ='DAT' THEN ',' WHEN TATAFOR ='TST' THEN ',' WHEN TATAFOR ='INT' THEN ',' WHEN TATAFOR ='FLO' THEN ',' WHEN TATAFOR ='VCH' THEN ' ( '|| RTRIM(CHAR(TATALON))||' ), ' END, TATAORD FROM DEADIC.ATRIBUTO_03

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

WHERE ENTOR='DE' AND TATACLE=&CODIGO_ENTIDAD UNION ALL SELECT ');', 999 AS TATAORD FROM DEADIC.ENTIDAD_16 WHERE TENTCLA=&CODIGO_ENTIDAD AND ENTOR='DE' UNION ALL SELECT 'COMMENT ON TABLE '||RTRIM(TENTNOM) ||' IS '''||RTRIM(TENTDEF)||''';', 1000 AS TATAORD FROM DEADIC.ENTIDAD_16 WHERE TENTCLA=&CODIGO_ENTIDAD AND ENTOR='DE' UNION ALL

SELECT 'COMMENT ON COLUMN '||RTRIM(TENTNOM) ||'.'||RTRIM(TATANOM)||' IS '''||RTRIM(TATADEF)||''';' , TATAORD + 1000 AS TATAORD FROM DEADIC.ENTIDAD_16 A, DEADIC.ATRIBUTO_03 B WHERE A.ENTOR=B.ENTOR AND TENTCLA=TATACLE AND A.ENTOR='DE' AND TATACLE=&CODIGO_ENTIDAD ) SELECT A FROM DATOS ORDER BY B

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Cambio de Campos

Queries

Modelado

Modelo E/R Cambio Normalización

Queries

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

¿Qué es NOSQL?

NOSQL es una evolución del sistema clásico de bases de datos relacionales cuya principal característica es que no se requiere una definición inicial de las estructuras sobre las que se almacenarán los datos.

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

¿Por qué se llega a NOSQL?

• Evolución del modelo entidad-relación para el soporte de estructuras variables en el tiempo. • Alternativas • • •

Meta-modelos en ER Bases de datos de objetos Bases de datos XML / XQuery

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Tipos de Bases de Datos NOSQL

• Columna •

Hadoop/HBase, Cassandra

• Documento •

MongoDB, CouchDB

• Clave/Valor •

DynamoDB, Redis

• Grafos •

Neo4j, Infinite Graph http://nosql-database.org

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Quiero usar NOSQL… dame ejemplos

• Ejemplo Básico… • •

Información de redes sociales Logs

• … y en la industria • Herencia y Polimorfismo de datos

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

¿Polimorfismo y NOSQL? Domicilio Persona Nombre Apellido1 Apellido2

Calle Comunidad País Domicilio

Persona Nombre Middle Name Apellido

Calle Condado País

Producto 1

Producto 2

Producto3

Valor Inicial Valor Final

Valor Estado Demandante

Regla

Domicilio Calle Región País

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Hablemos de MongoDB

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

¿Qué es MongoDB?

• MongoDB es una base de datos NOSQL orientada a documentos. • Desarrollada por 10gen (ahora llamados MongoDB).

• Características • Flexibilidad de los Modelos • Alto Rendimiento, Alta Disponibilidad • Escalabilidad

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

BSON su modelo de documentos

• El modelo de documentos se basa en el estándar JSON/BSON. • BSON es una serialización binaria del JSON • Características de BSON • Permite tener documentos dentro de documentos • Podemos definir arrays dentro de documentos

http://bsonspec.org/

Leganés 6-7 Febrero 2013 4

{

Primeros Pasos en MongoDB

Documento BSON _id: "joe", name: "Joe Bookreader", addresses: [ { street: "123 Fake Street", city: "Faketon", state: "MA", zip: 12345 }, { street: "1 Some Other Street", city: "Boston", state: "MA", zip: 12345 } ]}

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Instalar MongoDB

• Distribuciones para MacOS, Linux (Ubuntu, Debian,…) y Windows. Ejecuta en plataformas 32-bits y 64-bits • Se puede descargar desde http://www.mongodb.org/downloads • Documentación de instalación en http://docs.mongodb.org/manual/installation/

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Ejecutar MongoDB

mongod Nos permite arrancar el servidor de MongoDB Escucha en el puerto 27017 mongo Ejecuta la consola de MongoDB En http://www.mongodb.org/downloads se puede encontrar una consola de pruebas.

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Conceptos Básicos SQL Terms/Concepts

MongoDB Terms/Concepts

database

database

table

collection

row

document or BSON document

column

field

index

index

table joins

embedded documents and linking

primary key Specify any unique column or column combination as primary key.

primary key In MongoDB, the primary key is automatically set to the _id field.

aggregation (e.g. group by)

aggregation pipeline See the SQL to Aggregation Mapping Chart.

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Pero haz ya una SELECT

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Modelo de pruebas {

"_id" : ObjectId("525ab02733b01a66a9dcbc60"), "ciudad" : "Salamanca", "habitantes" : 567433, "monumentos": [“Catedral”, “Universidad”, “Patio Chico”], "patrimonio“: true } { "_id" : ObjectId("525ab02733b01a66a9dcbc61"), "ciudad" : "Murcia", "habitantes" : 441354 “geo" : { “lat" : 40.942903200, “long" : -4.108806900 }

}

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Modelo de pruebas { "_id" : ObjectId("525ab02733b01a66a9dcbc62"), "ciudad" : “Avila", "habitantes" : 407648 “restaurantes”: [ { “nombre”:”La Bruja” “Direccion “: “Paseo del Rastro, 4 " }, { “nombre”:”La Casona” “Direccion “: “Plaza Pedro Dávila, 6 " } ] }

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Consultas, función find

Las consultas se ejecutan mediante la función find: db.ciudades.find({ciudad:'Madrid'});

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Projection o selección de datos

Projection nos permite escoger que campos devolver del documento db.ciudades.find({ habitantes:{$gt:1000000}}, {ciudad:1}); db.ciudades.find({ habitantes:{$gt:1000000}}, {ciudad:1,_id:0});

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Dot Notation

MongoDB utiliza la anotación del punto para acceder a campos de subdocumentos y a elementos de arrays: array.indice o subdocumento.campo

db.ciudades.find({ geo.lat:{$gt:40.1}}); db.ciudades.find({ monumentos:'Catedral'); db.ciudades.find({ monumentos.0:'Catedral');

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Consultas con operadores

Podemos utilizar operadores dentro de las consultas: db.ciudades.find({ habitantes:{$lt:400000} }); db.ciudades.find({ habitantes:{$gt:1000000} });

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Combinar operadores

El documento de consulta puede combinar varios operadores: db.ciudades.find({ ciudad:/^M/, habitantes:{$lt:1000000} }); db.ciudades.find({ $or:[{ciudad:/^M/},{ciudad:/^Z/}] });

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Sort para ordenar resultados

Método sort: Ascendente (1) o descendente (-1) db.ciudades.find().sort({ ciudad:1 }); db.ciudades.find().sort({ ciudad:-1 });

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Limit para limitar las consultas

Recibe el número de elementos a limitar en la consulta como parámetro. db.ciudades.find().limit(4);

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

IN para consulta de elementos

Los elementos de los valores están en un array: db.ciudades.find({ciudad: {$in:['Avila','Zamora','Madrid']}});

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Busquedas por Like

Uso de expresiones regulares para buscar por like: db.ciudades.find({ciudad:/^M/}); db.ciudades.find({ciudad:/r/}); db.ciudades.find({ciudad:/d$/});

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Saltarnos datos con SKIP

Recibe como parámetro los elementos a saltar: db.ciudades.find().skip(2);

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Insertar datos

Utilizamos la función Insert db.ciudades.insert( {'ciudad':'Avila', 'habitantes':58915 });

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Actualizar datos

Utilizamos la función Update: db.ciudades.update( {ciudad:'Avila'}, {$set:{habitantes:58915} });

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Actualizaciones Múltiples

Por defecto MongoDB solo actualiza un documento: db.ciudades.update({ habitantes:{$gt:600000}}, {$set:{'grande':true}}, {multi:true} );

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Upsert: Actualiza si no Inserta

La función upsert actualiza un documento, si no está lo inserta. db.ciudades.update({ ciudad:'Zamora'}, {$set:{habitantes:65362}}, {upsert:true} );

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Borrado de datos

Utiliza la función delete. Por defecto borra todos los documentos, al menos que especifiquemos otra cosa. db.ciudades.delete({ ciudad:'Zamora'}, ); db.ciudades.delete({ habitantes:{$lt:400000} },1);

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

$push, $pull, $addtoset,…

Operadores que nos sirven para el manejo de arrays: db.ciudades.update( { ciudad: “Avila" }, { $push: { monumentos: { $each: [ ‘Murallas’,’La Santa’,’San Vicente’] } }})

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Más funciones

Hay muchas más funciones básicas: • • • • •

Agrupación Distintc Contadores FindAndModify …

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

¿Tercera forma normal?

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Modelos de Datos

• Embebidos • Aprovecha las capacidades de los subdocumentos para contener la relación dentro del propio documento. • Normalizados • Referencia los documentos mediante IDs.

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Modelo Embebido

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Modelo Normalizado

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Cardinalidad en MongoDB

• Cardinalidades en MongoDB: • Modelo 1:1 • Modelo 1:N • Modelo 1:N Referenciado • Modelos en Árbol • Más información de modelos en http://docs.mongodb.org/manual/data-modeling/

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Cardinalidad 1:1

{ _id: "joe", name: "Joe Bookreader", address: { street: "123 Fake Street", city: "Faketon", state: "MA", zip: 12345 } }

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Cardinalidad 1:N {

}

_id: "joe", name: "Joe Bookreader", addresses: [ { street: "123 Fake Street", city: "Faketon", state: "MA", zip: 12345 }, { street: "1 Some Other Street", city: "Boston", state: "MA", zip: 12345 } ]

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Cardinalidad 1:N Referenciada

{

} {

}

_id: "oreilly", name: "O'Reilly Media", founded: 1980, location: "CA“ _id: 123456789, title: "MongoDB: The Definitive Guide“, author: [ "Kristina Chodorow", "Mike Dirolf" ], published_date: ISODate("2010-09-24"), pages: 216, language: "English", publisher_id: "oreilly“

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Quiero hacer un join

• MongoDB no soporta JOINS • La manipulación de información referenciada hay que hacerla manual.

• Algunos drivers han implementado un objeto DBRefs para poder ayudar en la consulta de información referenciada.

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

A tener en cuenta cuando modelas

• El crecimiento de los documentos •

Si añadimos nuevos elementos/campos al documento y nos pasamos del tamaño máximo MongoDB tendrá que alojar el documento en disco.

• Atomicidad de las operaciones •

En MongoDB la atomicidad es a nivel de documento.

• Manejo de Índices •

Para mejorar el rendimiento. Por defecto hay un índice sobre el _id.

• Distribución de los documentos •

El escalado en MongoDB es horizontal utilizando una «sharded key»

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Rendimiento… Indexame.

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

¿Cómo establecer un índice? •

MongoDB nos proporciona la función ensureIndex para establecer un índice.

db.ciudades.ensureIndex( { “ciudad" : 1 } )

• Podemos crear índices en subdocumentos db.people.ensureIndex({ “direccion.codigopostal": 1 } )

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Realizando un Explain

• Se puede ver el plan de ejecución de una consulta con explain db.collection.find().explain()

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Resultado de un Explain { "cursor" : "BasicCursor", "isMultiKey" : false, "n" : 14, "nscannedObjects" : 14, "nscanned" : 14, "nscannedObjectsAllPlans" : 14, "nscannedAllPlans" : 14, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 0, "indexBounds" : {} }

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Agregar datos en MongoDB

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

¿Qué es el Aggregation Framework?

• Son un conjunto de operaciones que nos permiten procesar conjuntos completos de resultados. • Evita que tengamos que desarrollar código de aplicación para realizar ciertas tareas.

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Pipeline del Aggregation Framework

• El Aggregation Framework se apoya en un pipeline de operaciones que se van ejecutando de manera consecutiva • Las operaciones que podemos ejecutar en un pipeline son: • Filtros • Transformaciones • Agrupaciones • Ordenaciones

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Secuencia del Pipeline

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

SQL versus Aggregation Framework SQL Terms, Functions, and Concepts

MongoDB Aggregation Operators

WHERE

$match

GROUP BY

$group

HAVING

$match

SELECT

$project

ORDER BY

$sort

LIMIT

$limit

SUM()

$sum

COUNT()

$sum

join

No direct corresponding operator; however, the $unwind operator allows for somewhat similar functionality, but with fields embedded within the document.

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Java, Python, Javascript,..

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Lenguajes de Programación con MongoDB

• MongoDB ofrece drivers para la integración con múltiples lenguajes: • Java • Python • PHP • C/C++ • Ruby • Scala • ….

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Shell MongoDB

• El manejo básico que podemos hacer es mediante el Shell MongoDB

• El Shell MongoDB es un shell Javascript que nos permite ejecutar las operaciones básicas • Para ejecutarlo mongo nombreprograma.js

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Programa con Shell conn= new Mongo(); db = conn.getDB("demografia"); printjson(db.ciudades.findOne());

cursor = db.ciudades.find( {$or:[{ciudad:/^M/},{ciudad:/^Z/}] }); while (cursor.hasNext()){ }

printjson(cursor.next());

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

MongoDB y Java import com.mongodb.*; MongoClient mongoClient = new MongoClient( "localhost" ); DBCollection coll =

db.getCollection("testCollection");

BasicDBObject doc = new BasicDBObject("name", "MongoDB“) . append("type", "database") . append("count", 1) . append("info", new BasicDBObject("x", 203).append("y", 102)); coll.insert(doc);

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Cursores en Java import com.mongodb.*; MongoClient mongoClient = new MongoClient( "localhost" ); DBCollection coll = db.getCollection("testCollection");

BasicDBObject query = new BasicDBObject("i", 71); cursor = coll.find(query); try { while(cursor.hasNext()) { System.out.println(cursor.next()); } } finally { cursor.close(); }

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

MongoDB, Python y Pymongo import pymongo client = MongoClient() db = client.demografia collection = db.ciudades ciudad = { ‘ciudad’:’Avila’, habitantes:58570 } ciudades.insert(ciudad)

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Más, más, más,...

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

MongoDB y GridFS

• GridFS • Manejo de documentos que excedan el límite de 16Mb establecido por BSON. • GridFS permite partir el documento en diferentes partes.

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

MongoDB y Map Reduce

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

MongoDB y Replicación

• Proporciona la alta disponibilidad y redundancia de datos.

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

MongoDB y Sharding

• Escalado Horizontal: Capacidad de distribuir datos en diferentes máquinas • Utiliza la sharding key para distribuir los datos.

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Herramientas para MongoDB

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Herramientas en MongoDB

• MongoBird • •

Administrador y Consola en tiempo real http://mongobird.citsoft.net/

• MongoVision •

Herramienta de Administración http://code.google.com/p/mongo-vision/

• MongoHub • • •

Herramienta nativa para MacOS https://github.com/fotonauts/MongoHub-Mac

¿Cómo administras MongoDB? http://codemotion.es/talk/18-october/122

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

MonjaDB

• Plugin para Elipse que permite administrar MongoDB • https://marketplace.eclipse.org/content/monjadb

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Quiero aprender más

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

MongoDB University

• https://education.mongodb.com

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Enlaces MongoDB

• Documentación de MongoDB •

http://docs.mongodb.org/manual/

• API MongoDB •

http://api.mongodb.org

• MongoDB en Facebook •

https://www.facebook.com/mongodb

• MongoDB en Twitter •

http://twitter.com/#!/MongoDB

• Grupos en Google •

https://groups.google.com/group/mongodb-user

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Enlaces MongoDB en Español

• MongoDB Meetup •

http://www.meetup.com/Madrid-MongoDB-User-Group/

• MongoDB en Español Facebook •

https://www.facebook.com/groups/mongodb.es/

• Mis artículos sobre MongoDB •

http://lineadecodigo.com/categoria/mongodb

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

No os preocupéis por los enlaces…

@victor_cuervo

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Leganés 6-7 Febrero 2013 4

Primeros Pasos en MongoDB

Get in touch

Social

© Copyright 2013 - 2024 MYDOKUMENT.COM - All rights reserved.