Luciano Ramalho
                                      luciano@ramalho.org
                                              @ramalhoorg




               Open Library no MongoDB


     Usando Map/Reduce e o Aggregation Framework
     para análise e modelagem de dados
Tuesday, July 17, 12
Temas
           • Sobre o projeto Open Library
           • Conversão e importação da massa de dados
           • Análise dos dados com o framework de agregação
           • Análise dos dados com Map	

/Reduce
           • Refatoração do modelo de dados para o
                   MongoDB
           • Encerramento
                                                     @ramalhoorg
Tuesday, July 17, 12
Sobre o projeto
                        Open Library



                                         @ramalhoorg
Tuesday, July 17, 12
Sobre a Open Library
           • Missão:
               “One web page for every book”
           • Um projeto do Internet Archive
           • 117.439.126 registros bibliográficos em jun/2012
           • Mais de 1.000.000 de e-books gratuitos para
                   baixar (livres, CC, domínio público etc.)

                                                               @ramalhoorg
Tuesday, July 17, 12
A Tecnologia da
       Open Library
         • Infobase: uma API Python para bases de dados
                 semi-estruturadas sobre tabelas normalizadas
                • também conhecida como ThingDB
         • Inclui versionamento de registros
         • Muitos join para recuperar uma entidade conceitual
         • Fortemente depenente do SOLR/Lucene para
                 exibir suas páginas
                                                           @ramalhoorg
Tuesday, July 17, 12
Modelo de dados
       semi-estruturado
           • Base teórica existe!
           • Palavras-chave para pesquisa:
                   semistructured ou semi-structured database

    “The semi­structured data model is designed as an
   evolution of the relational data model that allows the
     representation of data with a flexible structure. ”
                                    SUCIU, Dan. Semi­Structured Data Model.
                                           In: LIU, L. Encyclopedia of Database Systems
                                                                           @ramalhoorg
Tuesday, July 17, 12
Data on the
       Web (1999)
           • From Relations to
                   Semistructured Data
                   and XML
           • Autores: Abiteboul,
                   Buneman & Suciu
           • Notação apresentada:
                   semelhante a JSON

                                         @ramalhoorg
Tuesday, July 17, 12
Semistructured
       Database Design
       (2004)
           • Autores: Ling, Lee & Dobbie
           • Algoritmos de normalização
                   sem a 1ª Forma Normal
                   (N1NF = Non First Normal
                   Form)

                                              @ramalhoorg
Tuesday, July 17, 12
Conversão e
                       importação da
                       massa de dados



                                        @ramalhoorg
Tuesday, July 17, 12
Massa de dados
           • OL Complete Dump: ol_cdump_latest.txt.gz*
           • 118.598.056 linhas em 1/jun/2012
           • 16 GB comprimidos (.gz), 91 GB sem compressão
           • 32 tipos diferentes de registros
            • 1.158.930 (~1%) não são registros bibliográficos
           • Inclui todas as revisões de todos os registros
                       * http://openlibrary.org/developers/dumps
                                                         @ramalhoorg
Tuesday, July 17, 12
Converter para carregar
           • Escolha de uma chave primária (campo _id)
            • Chave composta: key+"-"+revision
              • /books/OL1656964M-1
           • Opção adotada: a conversão mais simples possível
            • Usar JSON do dump, acrescido de campo _id

                                                       @ramalhoorg
Tuesday, July 17, 12
Carga: conversor_ol.py
        import sys
        import json
        import io

        def conv_linha(lin, indent=None):
            rec_type, rec_key, rec_revision, rec_modified, rec_json = lin.split(u't')
            rec = json.loads(rec_json)
            rec[u'_id'] = rec_key + u'-' + rec_revision
            return json.dumps(rec, indent=indent)

        def conv_arquivo(nome_arq, max_lin=sys.maxsize, indent=None):
            with io.open(nome_arq, encoding='utf-8') as arq:
                for num_lin, lin in enumerate(arq, 1):
                    if not lin.strip():
                        continue
                    saida = conv_linha(lin, indent)
                    print saida.encode('utf-8')
                    if num_lin >= max_lin:
                        break

        if __name__=='__main__':
            if len(sys.argv) == 2:
                converte_arquivo(sys.argv[1])
            else:
                print 'Modo de usar: %s <ol_dump_file>' % __name__


                                  * https://github.com/ramalho/mongosp
                                                                @ramalhoorg
Tuesday, July 17, 12
Usando mongoimport
  python conversor_ol.py $1 | 
    mongoimport -d openlibrary -c complete --stopOnError

           • -d: database
           • -c: collection
           • --stopOnError: interromper se houver erro
           • --upsert: sobrescrever ao importar _id duplicado
                   (default: ignorar o novo registro)
           • --file: arquivo a inserir (default: stdin)
                                                         @ramalhoorg
Tuesday, July 17, 12
Análise dos dados usando
         o framework de agregação



                              @ramalhoorg
Tuesday, July 17, 12
Indexação para análise

           • Criar índices esparsos para:
            • key
            • revision
            • type
            • outros...
                                            @ramalhoorg
Tuesday, July 17, 12
Aggregation Framework:
       o básico
           • Novidade no MongoDB 2.1/2.2
           • Alternativa ao Map/Reduce
           • Mais fácil de usar
           • Melhor desempenho
            • implementado em C++, usa threads
                       (Map/Reduce depende do interpretador
                       JavaScript Spidermonkey, mono-thread)
                                                               @ramalhoorg
Tuesday, July 17, 12
Exemplo: group_types.js
      db = db.getMongo().getDB('openlibrary');
      db.complete.ensureIndex({"type.key":1});

      var res = db.complete.aggregate(
          { "$group" : {
              ! "_id" : "$type.key",

          }},
              ! "qt" : { "$sum" : 1 }               • $group
      );
          { "$sort" : { "qt" : -1 }}                • $sort
      res.result.forEach(function (r) {
          print(r.qt+"t"+r._id);
      });

                       * https://github.com/ramalho/mongosp
                                                     @ramalhoorg
Tuesday, July 17, 12
Exemplo: group_types.js
                                  $ time mongo2.1

       • O primeiro lote
                                  group_types.js
                                  MongoDB shell version: 2.1.2
               de 1.000.000 de    connecting to: test
                                  605781! /type/edition
               registros tem 9    382428! /type/author
               tipos diferentes   9211!/type/work
                                  1935!/type/redirect
       • Os três primeiros        623! /type/delete
                                  7! /type/template
               são os mais        7! /type/page
               importantes:       5! /type/doc
               edition, author,   3! /type/macro
               work               real!0m23.658s
                                  user!0m0.030s
                                  sys! 0m0.004s        @ramalhoorg
Tuesday, July 17, 12
Agregação em estágios
           • Estágios: etapas em um fluxo (steps in a pipeline)
           • Estágios são executados em ordem, na ordem dos
                   parâmetros da invocação de mapReduce
           • Cada estágio aplica um operador especial
           • O mesmo operador pode ser usado várias vezes
                   em estágios diferentes


                                                          @ramalhoorg
Tuesday, July 17, 12
Operadores de estágios
           • $match     • $group
           • $project   • $sort
           • $limit     • $unwind
           • $skip


                                    @ramalhoorg
Tuesday, July 17, 12
Exemplo 2a
                       * https://github.com/ramalho/mongosp
      var res = db.complete.aggregate(
          { $match : {"type.key" : "/type/edition"} },
          { $project : { languages : 1} },
          { $unwind : "$languages" },
          { $group : {
              _id : "$languages.key",            • $match

          }},
              qt : { $sum : 1 }
                                                 • $project

      );
          { $sort : { qt : -1, _id : 1 }}
                                                 • $unwind
                                                 • $group
                                                 • $sort
                                                    @ramalhoorg
Tuesday, July 17, 12
Exemplo 2b
                              * https://github.com/ramalho/mongosp
                  db = db.getMongo().getDB('openlibrary');
                  db.complete.ensureIndex({"revision":1});
                  var res = db.complete.aggregate(
                      { $match : {"type.key" : "/type/edition"} },

                                                           •
                      { $project : { languages : 1} },
                      { $unwind : "$languages" },           $group
                      { $group : {
                          _id : "$languages.key",
                          qt : { $sum : 1 }
                                                           •$sort
                      }},
                      { $sort : { qt : -1, _id : 1 }}
                  );
                  res.result.forEach(function (r) {
                      print(r.qt+"t"+r._id);
                  });                                      @ramalhoorg
Tuesday, July 17, 12
O que não dá para fazer
       (atualmente)
            • Conjunto limitado de operadores
            • Para lidar com strings, por exemplo:
             • $substr, $toLower, $toUpper, $strcasecmp
             • não tem length, regex, startswith, etc.
            • O framework foi feito para ser extensível
             • Mas não tem uma arquitetura de plug-ins
                                                      @ramalhoorg
Tuesday, July 17, 12
Análise dos dados com
                            Map/Reduce



                                          @ramalhoorg
Tuesday, July 17, 12
O problema do
       “schema after”
           • Conceito: “schema before” x “schema after”
            • Michael Stonebraker (criou Ingres,VoltDB etc):
           • MongoDB é “schema after”
           • Em uma base “schema after” em produção, o
                   esquema real quase nunca é exatamente o
                   planejado

                                                             @ramalhoorg
Tuesday, July 17, 12
Análise profunda
       dos dados
           • Estatísticas sobre a estrutura dos registros
            • para cada tipo de registro, quais campos
                       ocorrem, e em qual frequência
           • Estatísticas sobre estrutura dos campos
            • valores simples, arrays e documentos aninhados
                       (objetos)


                                                            @ramalhoorg
Tuesday, July 17, 12
Map/Reduce: o básico
           • Executado através do método mapReduce:
               db.complete.mapReduce(map, reduce,
                    {out: { inline : 1}, jsMode: true})

           • Função map deve processar cada item (this) e
                   emitir um par de chave: valor
           • Função reduce deve aceitar chave e um array de
                   valores, e devolver apenas um valor agregado

                                                             @ramalhoorg
Tuesday, July 17, 12
Map/Reduce




                       @ramalhoorg
Tuesday, July 17, 12
Map/Reduce
       me lembra
       Pacman
           • Jogador faz reduce
                   dos pontinhos
           • Resultado do reduce
                   é o score


                                   @ramalhoorg
Tuesday, July 17, 12
Exemplo                            284396!
                                          251678!
                                                    subtitle
                                                    subject_place
                                          592707!   lc_classifications
                                          264695!   contributions
                                          605777!   title
                                          604455!   languages

           • Obter lista de todos         475865!
                                          598671!
                                                    subjects
                                                    publish_country
                   os campos e quantas    193955!   series
                   vezes cada um          113818!   title_prefix
                                          605781!   type
                   ocorre nos registros   538357!   by_statement
                   de edition             605781!   revision
                                          600934!   publishers
                                          605781!   last_modified
                                          605781!   key

                                                               @ramalhoorg
Tuesday, July 17, 12
Map
           • Se o registro é do tipo edition, emitir um par de
                   («nome_do_campo», 1) para cada campo


                       var map = function () {
                           if (this.type.key === "/type/edition") {
                               for (field_name in this) {
                                   emit(field_name, 1);
                               }
                           }
                       }


                                                              @ramalhoorg
Tuesday, July 17, 12
Reduce
           • Todos os pares de («chave»,        «valor»)
                   são agrupados em pares pela «chave»:
                   («chave»: [«valor0», «valor1», «valor2»])

           •       A função reduce deve reduzir cada
                   «array_de_valores» a um único valor

               var reduce = function (key, values) {
                   var total = 0;
                   values.forEach(function(n) { total += n; });
                   return total;
               }
                                                         @ramalhoorg
Tuesday, July 17, 12
Executar mapReduce
                       var res = db.complete.mapReduce(map, reduce, {
                           "out": { "inline" : 1},
                           "jsMode": true
                       });

                       //exibir resultado
                       res.results.forEach(function (r) {
                           print(r.value+"t"+r._id);
                       });
                       print("-----");
                       for (var chave in res.counts) {
                           if (chave !== "_id") {
                               print(chave+"t"+res.counts[chave]);
                           }
                       }
                       print("-----");
                       print("tempo (s)t"+res.timeMillis/1000);
                                                              @ramalhoorg
Tuesday, July 17, 12
Resultado de
     mapReduce    • Usando                     {"out":
  > var res = db.complete.mapReduce(map,         { "inline" : 1}}
  ...   reduce, {"out": { "inline" : 1},
  ...           "jsMode": true });
  {                                        [...]
  "results" : [                            !   !   {
  !   !  {                                 !   !   !   "_id" : "works",
  !   !  !   "_id" : "_id",                !   !   !   "value" : 4415
  !   !  !   "value" : 605781              !   !   }
  !   !  },                                !   ],
  !   !  {                                 !   "timeMillis" : 156659,
  !   !  !   "_id" : "authors",            !   "counts" : {
  !   !  !   "value" : 469305              !   !   "input" : 1000000,
  !   !  },                                !   !   "emit" : 13196408,
  !   !  {                                 !   !   "reduce" : 363448,
  !   !  !   "_id" : "by_statement",       !   !   "output" : 61
  !   !  !   "value" : 538357              !   },
  !   !  },                                !   "ok" : 1,
  !   !  {                                 }
  !   !  !   "_id" : "classifications",    real! 2m36.696s
  !   !  !   "value" : 3                   user! 0m0.028s
  !   !  },                                sys!0m0.005s
                                                                     @ramalhoorg
Tuesday, July 17, 12
Executar mapReduce
                       * https://github.com/ramalho/mongosp
  $ mongo2.1 mr_fields.js        210! ! coverimage
  MongoDB shell version:         16! ! isbn_odd_length
                                    !
  2.1.2                          3! ! ! classifications
  connecting to: test            1! ! ! collections
  284396! subtitle               5! ! ! copyright_date
  251678! subject_place          4! ! ! download_url
  592707! lc_classifications     3! ! ! purchase_url
  264695! contributions          1! ! ! language_code
  605777! title                  -----
  604455! languages              input! 1000000
                                       !
  475865! subjects               emit!! 13196408
  598671! publish_country        reduce! 363448
  193955! series                 output! 61
  113818! title_prefix           -----
  605781! type                   tempo (s)! 102.13 @ramalhoorg
Tuesday, July 17, 12
$ mongo2.1 mr_fieldtypes_sort.js
                                MongoDB shell version: 2.1.2

       Campos                   connecting to: test
                                _id:string!
                                          605781
                                authors:array! 469305

       e tipos                  by_statement:string! 538357
                                classifications:object!3
                                collections:array!1
                                contributions:array! 264695
                                copyright_date:string! 5
      • Identificar tipo         coverimage:string!210
                                covers:array!3648
              do dado em        created:object! 10086
              cada ocorrência   description:object! 33040
                                dewey_decimal_class:array!325519
      • Detectar                download_url:array! 4
                                edition_name:string! 124615
              inconsistências   first_sentence:object! 682
                                first_sentence:string! 2

                            * https://github.com/ramalho/mongosp
                                                          @ramalhoorg
Tuesday, July 17, 12
Refatorando o esquema
                    para o MongoDB



                                    @ramalhoorg
Tuesday, July 17, 12
{
                                 "subtitle": "Ausbau und Planung der petrochemischen und energieintensiven
                               Industrien zum Zeitpunkt des zweiten Golfkriegs",




       Um registro
                                 "subject_place": [
                                    "Middle East."
                                 ],
                                 "lc_classifications": [
                                    "HD9579.C33 M6284 1991"
                                 ],
                                 "contributions": [
                                    "Helmschrott, Helmut."
                                 ],
                                 "title": "Industrialisierung der arabischen OPEC-Lau0308nder und des Iran",
                                 "languages": [
                                    {
                                      "key": "/languages/ger"
                                    }
                                 ],
                                 "subjects": [
                                    "Petroleum chemicals industry -- Middle East.",
                                    "Petroleum industry and trade -- Middle East.",
                                    "Gas industry -- Middle East."
                                 ],
                                 "publish_country": "gw ",
                                 "series": [
                                    "Ifo Forschungsberichte der Abteilung Entwicklungslau0308nder ;",
                                    "Nr. 74",
                                    "Ifo Forschungsberichte der Abteilung Entwicklungslau0308nder ;",
                                    "74."
                                 ],
                                 "title_prefix": "Die ",
                                 "type": {
                                    "key": "/type/edition"
                                 },




           • 25 campos neste
                                 "by_statement": "von Axel J. Halbach, Helmut Helmschrott.",
                                 "revision": 1,
                                 "publishers": [
                                    "Ifo Institut fuu0308r Wirtschaftsforschung",
                                    "Weltforum Verlag"
                                 ],
                                 "last_modified": {



                   registro
                                    "type": "/type/datetime",
                                    "value": "2008-04-01T03:28:50.625462"
                                 },
                                 "key": "/books/OL1656964M",
                                 "authors": [
                                    {
                                      "key": "/authors/OL45038A"
                                    }
                                 ],
                                 "publish_places": [
                                    "Muu0308nchen"
                                 ],
                                 "pagination": "viii, 270 p. :",
                                 "lccn": [
                                    "91218377"
                                 ],
                                 "notes": {
                                    "type": "/type/text",
                                    "value": "Includes bibliographical references (p. 268-270)."
                                 },
                                 "number_of_pages": 270,
                                 "isbn_10": [
                                    "3803903955"
                                 ],
                                 "publish_date": "1991",
                                 "_id": "/books/OL1656964M-1"
                               }


                                                                                       @ramalhoorg
Tuesday, July 17, 12
Chave estrangeira
                       "title": "Industrialisierung der arabischen...",
                       "revision": 1,
                       "publishers": [
                          "Ifo Institut fuu0308r Wirtschaftsforschung",
                          "Weltforum Verlag"
                       ],
                       "last_modified": {
                          "type": "/type/datetime",
                          "value": "2008-04-01T03:28:50.625462"
                       },
                       "key": "/books/OL1656964M",
                       "authors": [
                          {
                            "key": "/authors/OL45038A"
                          }
                       ],
                       "publish_places": [
                          "Muu0308nchen"
                       ],
                                                              @ramalhoorg
Tuesday, July 17, 12
Refatoração do esquema
           • Usar key+revision como chave primária _id
           • Manter campos key e revision separados
            • Para fazer pseudo-auto join recuperando o
                       histórico de um registro bibliográfico
           • Embutir (embed) campo nome do autor no
                   documento
                                              "authors": [
                                                 {
                                                   "key": "/authors/OL45038A",
                                                   "name": "W. A. Mozart"
                                                 }
                                              ],                     @ramalhoorg
Tuesday, July 17, 12
Representação do
       histórico de versões
           • Embutir pode ser uma boa opção para os tipos de
                   registros que são raramente atualizados
                  • Versões antigas embutidas
           • Para registros que sofrem muitas atualizações, a
                   melhor opção é uma sequência de referências
                   (“chaves estrangeiras”)
                  • Um “pseudo self-join” pode ser feito pelo
                       atributo key para recuperar o histórico
                                                                 @ramalhoorg
Tuesday, July 17, 12
Integridade referencial
           • Identificação de problemas atuais
           • Ferramentas de suporte
            • Índices
            • Uso de um framework com ODM (object-
                       document mapper)
                  • Tarefas de monitoração assíncrona
                                                        @ramalhoorg
Tuesday, July 17, 12
Algumas dicas
           • Todo registro deve ter campos identificando:
            • seu tipo
            • a versão do esquema usada naquele registro
           • Mudanças no esquema podem ser feitas de modo
                   incremental, quando um documento é alterado
           • Use um ODM (Object-Document Mapper) para
                   aumentar a consistência dos dados armazenados

                                                           @ramalhoorg
Tuesday, July 17, 12
Tuesday, July 17, 12
Excelente opção para
                         hospedagem de MongoDB.
                        Pequenas instâncias gratuitas,
                       instâncias maiores por preços
                        acessíveis, sem você precisar
                        gerenciar o servidor, sistema
                          operacional, storage etc.
Tuesday, July 17, 12

Open Library no Mongodb

  • 1.
    Luciano Ramalho luciano@ramalho.org @ramalhoorg Open Library no MongoDB Usando Map/Reduce e o Aggregation Framework para análise e modelagem de dados Tuesday, July 17, 12
  • 2.
    Temas • Sobre o projeto Open Library • Conversão e importação da massa de dados • Análise dos dados com o framework de agregação • Análise dos dados com Map /Reduce • Refatoração do modelo de dados para o MongoDB • Encerramento @ramalhoorg Tuesday, July 17, 12
  • 3.
    Sobre o projeto Open Library @ramalhoorg Tuesday, July 17, 12
  • 4.
    Sobre a OpenLibrary • Missão: “One web page for every book” • Um projeto do Internet Archive • 117.439.126 registros bibliográficos em jun/2012 • Mais de 1.000.000 de e-books gratuitos para baixar (livres, CC, domínio público etc.) @ramalhoorg Tuesday, July 17, 12
  • 5.
    A Tecnologia da Open Library • Infobase: uma API Python para bases de dados semi-estruturadas sobre tabelas normalizadas • também conhecida como ThingDB • Inclui versionamento de registros • Muitos join para recuperar uma entidade conceitual • Fortemente depenente do SOLR/Lucene para exibir suas páginas @ramalhoorg Tuesday, July 17, 12
  • 6.
    Modelo de dados semi-estruturado • Base teórica existe! • Palavras-chave para pesquisa: semistructured ou semi-structured database “The semi­structured data model is designed as an evolution of the relational data model that allows the representation of data with a flexible structure. ” SUCIU, Dan. Semi­Structured Data Model. In: LIU, L. Encyclopedia of Database Systems @ramalhoorg Tuesday, July 17, 12
  • 7.
    Data on the Web (1999) • From Relations to Semistructured Data and XML • Autores: Abiteboul, Buneman & Suciu • Notação apresentada: semelhante a JSON @ramalhoorg Tuesday, July 17, 12
  • 8.
    Semistructured Database Design (2004) • Autores: Ling, Lee & Dobbie • Algoritmos de normalização sem a 1ª Forma Normal (N1NF = Non First Normal Form) @ramalhoorg Tuesday, July 17, 12
  • 9.
    Conversão e importação da massa de dados @ramalhoorg Tuesday, July 17, 12
  • 10.
    Massa de dados • OL Complete Dump: ol_cdump_latest.txt.gz* • 118.598.056 linhas em 1/jun/2012 • 16 GB comprimidos (.gz), 91 GB sem compressão • 32 tipos diferentes de registros • 1.158.930 (~1%) não são registros bibliográficos • Inclui todas as revisões de todos os registros * http://openlibrary.org/developers/dumps @ramalhoorg Tuesday, July 17, 12
  • 11.
    Converter para carregar • Escolha de uma chave primária (campo _id) • Chave composta: key+"-"+revision • /books/OL1656964M-1 • Opção adotada: a conversão mais simples possível • Usar JSON do dump, acrescido de campo _id @ramalhoorg Tuesday, July 17, 12
  • 12.
    Carga: conversor_ol.py import sys import json import io def conv_linha(lin, indent=None): rec_type, rec_key, rec_revision, rec_modified, rec_json = lin.split(u't') rec = json.loads(rec_json) rec[u'_id'] = rec_key + u'-' + rec_revision return json.dumps(rec, indent=indent) def conv_arquivo(nome_arq, max_lin=sys.maxsize, indent=None): with io.open(nome_arq, encoding='utf-8') as arq: for num_lin, lin in enumerate(arq, 1): if not lin.strip(): continue saida = conv_linha(lin, indent) print saida.encode('utf-8') if num_lin >= max_lin: break if __name__=='__main__': if len(sys.argv) == 2: converte_arquivo(sys.argv[1]) else: print 'Modo de usar: %s <ol_dump_file>' % __name__ * https://github.com/ramalho/mongosp @ramalhoorg Tuesday, July 17, 12
  • 13.
    Usando mongoimport python conversor_ol.py $1 | mongoimport -d openlibrary -c complete --stopOnError • -d: database • -c: collection • --stopOnError: interromper se houver erro • --upsert: sobrescrever ao importar _id duplicado (default: ignorar o novo registro) • --file: arquivo a inserir (default: stdin) @ramalhoorg Tuesday, July 17, 12
  • 14.
    Análise dos dadosusando o framework de agregação @ramalhoorg Tuesday, July 17, 12
  • 15.
    Indexação para análise • Criar índices esparsos para: • key • revision • type • outros... @ramalhoorg Tuesday, July 17, 12
  • 16.
    Aggregation Framework: o básico • Novidade no MongoDB 2.1/2.2 • Alternativa ao Map/Reduce • Mais fácil de usar • Melhor desempenho • implementado em C++, usa threads (Map/Reduce depende do interpretador JavaScript Spidermonkey, mono-thread) @ramalhoorg Tuesday, July 17, 12
  • 17.
    Exemplo: group_types.js db = db.getMongo().getDB('openlibrary'); db.complete.ensureIndex({"type.key":1}); var res = db.complete.aggregate( { "$group" : { ! "_id" : "$type.key", }}, ! "qt" : { "$sum" : 1 } • $group ); { "$sort" : { "qt" : -1 }} • $sort res.result.forEach(function (r) { print(r.qt+"t"+r._id); }); * https://github.com/ramalho/mongosp @ramalhoorg Tuesday, July 17, 12
  • 18.
    Exemplo: group_types.js $ time mongo2.1 • O primeiro lote group_types.js MongoDB shell version: 2.1.2 de 1.000.000 de connecting to: test 605781! /type/edition registros tem 9 382428! /type/author tipos diferentes 9211!/type/work 1935!/type/redirect • Os três primeiros 623! /type/delete 7! /type/template são os mais 7! /type/page importantes: 5! /type/doc edition, author, 3! /type/macro work real!0m23.658s user!0m0.030s sys! 0m0.004s @ramalhoorg Tuesday, July 17, 12
  • 19.
    Agregação em estágios • Estágios: etapas em um fluxo (steps in a pipeline) • Estágios são executados em ordem, na ordem dos parâmetros da invocação de mapReduce • Cada estágio aplica um operador especial • O mesmo operador pode ser usado várias vezes em estágios diferentes @ramalhoorg Tuesday, July 17, 12
  • 20.
    Operadores de estágios • $match • $group • $project • $sort • $limit • $unwind • $skip @ramalhoorg Tuesday, July 17, 12
  • 21.
    Exemplo 2a * https://github.com/ramalho/mongosp var res = db.complete.aggregate( { $match : {"type.key" : "/type/edition"} }, { $project : { languages : 1} }, { $unwind : "$languages" }, { $group : { _id : "$languages.key", • $match }}, qt : { $sum : 1 } • $project ); { $sort : { qt : -1, _id : 1 }} • $unwind • $group • $sort @ramalhoorg Tuesday, July 17, 12
  • 22.
    Exemplo 2b * https://github.com/ramalho/mongosp db = db.getMongo().getDB('openlibrary'); db.complete.ensureIndex({"revision":1}); var res = db.complete.aggregate( { $match : {"type.key" : "/type/edition"} }, • { $project : { languages : 1} }, { $unwind : "$languages" }, $group { $group : { _id : "$languages.key", qt : { $sum : 1 } •$sort }}, { $sort : { qt : -1, _id : 1 }} ); res.result.forEach(function (r) { print(r.qt+"t"+r._id); }); @ramalhoorg Tuesday, July 17, 12
  • 23.
    O que nãodá para fazer (atualmente) • Conjunto limitado de operadores • Para lidar com strings, por exemplo: • $substr, $toLower, $toUpper, $strcasecmp • não tem length, regex, startswith, etc. • O framework foi feito para ser extensível • Mas não tem uma arquitetura de plug-ins @ramalhoorg Tuesday, July 17, 12
  • 24.
    Análise dos dadoscom Map/Reduce @ramalhoorg Tuesday, July 17, 12
  • 25.
    O problema do “schema after” • Conceito: “schema before” x “schema after” • Michael Stonebraker (criou Ingres,VoltDB etc): • MongoDB é “schema after” • Em uma base “schema after” em produção, o esquema real quase nunca é exatamente o planejado @ramalhoorg Tuesday, July 17, 12
  • 26.
    Análise profunda dos dados • Estatísticas sobre a estrutura dos registros • para cada tipo de registro, quais campos ocorrem, e em qual frequência • Estatísticas sobre estrutura dos campos • valores simples, arrays e documentos aninhados (objetos) @ramalhoorg Tuesday, July 17, 12
  • 27.
    Map/Reduce: o básico • Executado através do método mapReduce: db.complete.mapReduce(map, reduce, {out: { inline : 1}, jsMode: true}) • Função map deve processar cada item (this) e emitir um par de chave: valor • Função reduce deve aceitar chave e um array de valores, e devolver apenas um valor agregado @ramalhoorg Tuesday, July 17, 12
  • 28.
    Map/Reduce @ramalhoorg Tuesday, July 17, 12
  • 29.
    Map/Reduce me lembra Pacman • Jogador faz reduce dos pontinhos • Resultado do reduce é o score @ramalhoorg Tuesday, July 17, 12
  • 30.
    Exemplo 284396! 251678! subtitle subject_place 592707! lc_classifications 264695! contributions 605777! title 604455! languages • Obter lista de todos 475865! 598671! subjects publish_country os campos e quantas 193955! series vezes cada um 113818! title_prefix 605781! type ocorre nos registros 538357! by_statement de edition 605781! revision 600934! publishers 605781! last_modified 605781! key @ramalhoorg Tuesday, July 17, 12
  • 31.
    Map • Se o registro é do tipo edition, emitir um par de («nome_do_campo», 1) para cada campo var map = function () { if (this.type.key === "/type/edition") { for (field_name in this) { emit(field_name, 1); } } } @ramalhoorg Tuesday, July 17, 12
  • 32.
    Reduce • Todos os pares de («chave», «valor») são agrupados em pares pela «chave»: («chave»: [«valor0», «valor1», «valor2»]) • A função reduce deve reduzir cada «array_de_valores» a um único valor var reduce = function (key, values) { var total = 0; values.forEach(function(n) { total += n; }); return total; } @ramalhoorg Tuesday, July 17, 12
  • 33.
    Executar mapReduce var res = db.complete.mapReduce(map, reduce, { "out": { "inline" : 1}, "jsMode": true }); //exibir resultado res.results.forEach(function (r) { print(r.value+"t"+r._id); }); print("-----"); for (var chave in res.counts) { if (chave !== "_id") { print(chave+"t"+res.counts[chave]); } } print("-----"); print("tempo (s)t"+res.timeMillis/1000); @ramalhoorg Tuesday, July 17, 12
  • 34.
    Resultado de mapReduce • Usando {"out": > var res = db.complete.mapReduce(map, { "inline" : 1}} ... reduce, {"out": { "inline" : 1}, ... "jsMode": true }); { [...] "results" : [ ! ! { ! ! { ! ! ! "_id" : "works", ! ! ! "_id" : "_id", ! ! ! "value" : 4415 ! ! ! "value" : 605781 ! ! } ! ! }, ! ], ! ! { ! "timeMillis" : 156659, ! ! ! "_id" : "authors", ! "counts" : { ! ! ! "value" : 469305 ! ! "input" : 1000000, ! ! }, ! ! "emit" : 13196408, ! ! { ! ! "reduce" : 363448, ! ! ! "_id" : "by_statement", ! ! "output" : 61 ! ! ! "value" : 538357 ! }, ! ! }, ! "ok" : 1, ! ! { } ! ! ! "_id" : "classifications", real! 2m36.696s ! ! ! "value" : 3 user! 0m0.028s ! ! }, sys!0m0.005s @ramalhoorg Tuesday, July 17, 12
  • 35.
    Executar mapReduce * https://github.com/ramalho/mongosp $ mongo2.1 mr_fields.js 210! ! coverimage MongoDB shell version: 16! ! isbn_odd_length ! 2.1.2 3! ! ! classifications connecting to: test 1! ! ! collections 284396! subtitle 5! ! ! copyright_date 251678! subject_place 4! ! ! download_url 592707! lc_classifications 3! ! ! purchase_url 264695! contributions 1! ! ! language_code 605777! title ----- 604455! languages input! 1000000 ! 475865! subjects emit!! 13196408 598671! publish_country reduce! 363448 193955! series output! 61 113818! title_prefix ----- 605781! type tempo (s)! 102.13 @ramalhoorg Tuesday, July 17, 12
  • 36.
    $ mongo2.1 mr_fieldtypes_sort.js MongoDB shell version: 2.1.2 Campos connecting to: test _id:string! 605781 authors:array! 469305 e tipos by_statement:string! 538357 classifications:object!3 collections:array!1 contributions:array! 264695 copyright_date:string! 5 • Identificar tipo coverimage:string!210 covers:array!3648 do dado em created:object! 10086 cada ocorrência description:object! 33040 dewey_decimal_class:array!325519 • Detectar download_url:array! 4 edition_name:string! 124615 inconsistências first_sentence:object! 682 first_sentence:string! 2 * https://github.com/ramalho/mongosp @ramalhoorg Tuesday, July 17, 12
  • 37.
    Refatorando o esquema para o MongoDB @ramalhoorg Tuesday, July 17, 12
  • 38.
    { "subtitle": "Ausbau und Planung der petrochemischen und energieintensiven Industrien zum Zeitpunkt des zweiten Golfkriegs", Um registro "subject_place": [ "Middle East." ], "lc_classifications": [ "HD9579.C33 M6284 1991" ], "contributions": [ "Helmschrott, Helmut." ], "title": "Industrialisierung der arabischen OPEC-Lau0308nder und des Iran", "languages": [ { "key": "/languages/ger" } ], "subjects": [ "Petroleum chemicals industry -- Middle East.", "Petroleum industry and trade -- Middle East.", "Gas industry -- Middle East." ], "publish_country": "gw ", "series": [ "Ifo Forschungsberichte der Abteilung Entwicklungslau0308nder ;", "Nr. 74", "Ifo Forschungsberichte der Abteilung Entwicklungslau0308nder ;", "74." ], "title_prefix": "Die ", "type": { "key": "/type/edition" }, • 25 campos neste "by_statement": "von Axel J. Halbach, Helmut Helmschrott.", "revision": 1, "publishers": [ "Ifo Institut fuu0308r Wirtschaftsforschung", "Weltforum Verlag" ], "last_modified": { registro "type": "/type/datetime", "value": "2008-04-01T03:28:50.625462" }, "key": "/books/OL1656964M", "authors": [ { "key": "/authors/OL45038A" } ], "publish_places": [ "Muu0308nchen" ], "pagination": "viii, 270 p. :", "lccn": [ "91218377" ], "notes": { "type": "/type/text", "value": "Includes bibliographical references (p. 268-270)." }, "number_of_pages": 270, "isbn_10": [ "3803903955" ], "publish_date": "1991", "_id": "/books/OL1656964M-1" } @ramalhoorg Tuesday, July 17, 12
  • 39.
    Chave estrangeira "title": "Industrialisierung der arabischen...", "revision": 1, "publishers": [ "Ifo Institut fuu0308r Wirtschaftsforschung", "Weltforum Verlag" ], "last_modified": { "type": "/type/datetime", "value": "2008-04-01T03:28:50.625462" }, "key": "/books/OL1656964M", "authors": [ { "key": "/authors/OL45038A" } ], "publish_places": [ "Muu0308nchen" ], @ramalhoorg Tuesday, July 17, 12
  • 40.
    Refatoração do esquema • Usar key+revision como chave primária _id • Manter campos key e revision separados • Para fazer pseudo-auto join recuperando o histórico de um registro bibliográfico • Embutir (embed) campo nome do autor no documento "authors": [ { "key": "/authors/OL45038A", "name": "W. A. Mozart" } ], @ramalhoorg Tuesday, July 17, 12
  • 41.
    Representação do histórico de versões • Embutir pode ser uma boa opção para os tipos de registros que são raramente atualizados • Versões antigas embutidas • Para registros que sofrem muitas atualizações, a melhor opção é uma sequência de referências (“chaves estrangeiras”) • Um “pseudo self-join” pode ser feito pelo atributo key para recuperar o histórico @ramalhoorg Tuesday, July 17, 12
  • 42.
    Integridade referencial • Identificação de problemas atuais • Ferramentas de suporte • Índices • Uso de um framework com ODM (object- document mapper) • Tarefas de monitoração assíncrona @ramalhoorg Tuesday, July 17, 12
  • 43.
    Algumas dicas • Todo registro deve ter campos identificando: • seu tipo • a versão do esquema usada naquele registro • Mudanças no esquema podem ser feitas de modo incremental, quando um documento é alterado • Use um ODM (Object-Document Mapper) para aumentar a consistência dos dados armazenados @ramalhoorg Tuesday, July 17, 12
  • 44.
  • 45.
    Excelente opção para hospedagem de MongoDB. Pequenas instâncias gratuitas, instâncias maiores por preços acessíveis, sem você precisar gerenciar o servidor, sistema operacional, storage etc. Tuesday, July 17, 12