Open Library no Mongodb
Upcoming SlideShare
Loading in...5
×
 

Open Library no Mongodb

on

  • 1,128 views

Uso de map/reduce e do framework de agregação do MongoDB para análise de uma grande base de dados bibliográfica

Uso de map/reduce e do framework de agregação do MongoDB para análise de uma grande base de dados bibliográfica

Statistics

Views

Total Views
1,128
Views on SlideShare
1,124
Embed Views
4

Actions

Likes
0
Downloads
13
Comments
0

2 Embeds 4

http://us-w1.rockmelt.com 3
http://www.twylah.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Open Library no Mongodb Open Library no Mongodb Presentation Transcript

    • Luciano Ramalho luciano@ramalho.org @ramalhoorg Open Library no MongoDB Usando Map/Reduce e o Aggregation Framework para análise e modelagem de dadosTuesday, 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 @ramalhoorgTuesday, July 17, 12
    • Sobre o projeto Open Library @ramalhoorgTuesday, 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.) @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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) @ramalhoorgTuesday, July 17, 12
    • Conversão e importação da massa de dados @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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(ut) 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 @ramalhoorgTuesday, 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) @ramalhoorgTuesday, July 17, 12
    • Análise dos dados usando o framework de agregação @ramalhoorgTuesday, July 17, 12
    • Indexação para análise • Criar índices esparsos para: • key • revision • type • outros... @ramalhoorgTuesday, 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) @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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 @ramalhoorgTuesday, July 17, 12
    • Operadores de estágios • $match • $group • $project • $sort • $limit • $unwind • $skip @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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); }); @ramalhoorgTuesday, 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 @ramalhoorgTuesday, July 17, 12
    • Análise dos dados com Map/Reduce @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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) @ramalhoorgTuesday, 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 @ramalhoorgTuesday, July 17, 12
    • Map/Reduce @ramalhoorgTuesday, July 17, 12
    • Map/Reduce me lembra Pacman • Jogador faz reduce dos pontinhos • Resultado do reduce é o score @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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); } } } @ramalhoorgTuesday, 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; } @ramalhoorgTuesday, 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); @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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 @ramalhoorgTuesday, July 17, 12
    • Refatorando o esquema para o MongoDB @ramalhoorgTuesday, 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" } @ramalhoorgTuesday, 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" ], @ramalhoorgTuesday, 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" } ], @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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 @ramalhoorgTuesday, 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