Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

João Batista Neto

Instalei o MongoDB, e agora ?

Recommended Posts

Você já seguiu os passos descritos para instalar o MongoDB e os drivers do MongoDB no PHP e está se perguntando, "E agora ?"

 

Bom, toda novidade tende a trazer muitas dúvidas e questões e, se tratando de NoSQL, não é diferente; Se mesmo após pesquisar bastante, você ainda tenha alguma das seguintes dúvidas:

 

"Não consigo imaginar um banco de dados, sem ser relacional"

"O que significa Schemaless ?"

"Pode, NoSQL algum dia, substituir o modelo relacional ?"

 

Se você continua se perguntando ou fazendo alguma afirmação semelhante às acima, então talvez, como desenvolvedor, eu consiga lhe mostrar o que significa utilizar um banco de dados como MongoDB.

 

Então, indo por partes:

 

http://forum.imasters.com.br/public/style_emoticons/default/seta.gif "Não consigo imaginar um banco de dados, sem ser relacional"

 

Essa talvez, seja a reação mais natural de quem passou um grande período lidando com bancos de dados relacionais e a DSL SQL, porém, vamos pensar a nível de código:

 

Código 1:

<?php
$usuarios = array(
array(
	'_id' => 1,
	'nome' => 'João Batista Neto'
)
);

$emails = array(
array(
	'_id' => 1,
	'id_usuarios' => 1,
	'email' => 'email1@exemplo.com'
),
array(
	'_id' => 2,
	'id_usuarios' => 1,
	'email' => 'email2@exemplo.com'
)
);

 

No código 1 temos 2 matrizes, a primeira contém uma lista de usuários e na segunda uma lista de emails para cada usuário; Para relacionar a matriz de usuários com a matriz de emails, utilizamos uma chave na matriz de emails chamada id_usuarios.

 

Código 2:

<?php
$usuarios = array(
	array(
	'_id' => 1,
	'nome' => 'João Batista Neto',
	'emails' => array( 'email1@exemplo.com' , 'emails2@exemplo.com' )
	)
);

 

Se você fosse representar, a nível de código, essa estrutura de lista de vários emails para um único usuário, você utilizaria o código 1 ou o código 2 ?

 

O que faz mais sentido para você ?

 

Bom, se você acha que o código 1 faz mais sentido para você, talvez você deva continuar com bancos de dados relacionais e a DSL SQL, porém, se a nível de código, você achar que faz mais sentido utilizar o código 2, seja bem vindo ao banco de dados não relacional e ao MongoDB.

 

MongoDB é um sistema de armazenamento orientado a coleções e documentos:

 

{
	"nome" : "João Neto",
	"idade" : 28,
	"email" : "email@exemplo.com"
};

Acima, temos um documento simples, com apenas 3 propriedades: nome, idade e email, uma coleção é uma lista desse documento.

 

http://forum.imasters.com.br/public/style_emoticons/default/seta.gif "O que significa Schemaless ?"

 

O grande ponto do MongoDB (e outros bancos de dados não relacional) é que ele é Schemaless.

 

"Schemaless ???"

 

Sim, ele não possui a rigidez pré-imposta por um schema, ou seja, eu posso ter uma nova propriedade para um novo documento a qualquer momento, por exemplo:

 

{
	"usuario" : "João Batista Neto",
	"email" : "neto@exemplo.com",
	"idade" : 28,
	"contatos" : [
		{
			"nome" : "Fulano",
			"email": "fulano@exemplo.com",
			"gtalk": "fulano@gmail.com"
		},
		{
			"nome" : "Beltrano",
			"email": "beltrano@test.com",
			"msn" : "msn@hotmail.com"
		},
	]
};

Perceba que Fulano possui um GTalk e que o Beltrano possui um MSN; Se estivéssemos trabalhando com um banco de dados com schema teríamos, necessariamente, que prever essa situação, seja criando uma tabela de atributos ou uma nova coluna na tabela de contatos.

 

De fato, não tem nenhum sentido, termos um schema para o banco de dados, afinal, modelamos nossa aplicação e as regras já existem a nível de aplicação; Se você não conseguiu engolir isso, pense na sua aplicação como sendo o schema do banco de dados, você define as regras à nível de aplicação e guarda o que quiser no banco de dados afinal, não tem qualquer sentido ter uma redundância de regras.

 

"E essa estrutura desse código acima, o que é isso ? Parece Javascript !!"

 

Pois, não apenas parece Javascript; O código acima, utilizado para ilustrar a situação é Javascript Object Notation ou JSON.

 

De fato, o próprio terminal do Mongo é um interpretador Javascript:

 

[neto@localhost ~]$ /opt/mongodb/bin/mongo
MongoDB shell version: 1.6.1
connecting to: test
> test = {
... 	"usuario" : "João Batista Neto",
... 	"email" : "neto@exemplo.com",
... 	"idade" : 28,
... 	"contatos" : [
... 	{
... 	"nome" : "Fulano",
... 	"email": "fulano@exemplo.com",
... 	"gtalk": "fulano@gmail.com"
... 	},
... 	{
... 	"nome" : "Beltrano",
... 	"email": "beltrano@test.com",
... 	"msn" : "msn@hotmail.com"
... 	},
... 	]
... };
{
 	"usuario" : "João Batista Neto",
 	"email" : "neto@exemplo.com",
 	"idade" : 28,
 	"contatos" : [
 	{
 	"nome" : "Fulano",
 	"email" : "fulano@exemplo.com",
 	"gtalk" : "fulano@gmail.com"
 	},
 	{
 	"nome" : "Beltrano",
 	"email" : "beltrano@test.com",
 	"msn" : "msn@hotmail.com"
 	}
 	]
}
>

E, para você que gosta de Javascript, procure brincar um pouco no terminal do Mongo, coisas interessantes como o código abaixo lhe farão se sentir mais a vontade:

 

> fatorial = function( X ){
... if ( X <= 2 ) return X;
... else return X * fatorial( X - 1 );
... }
function (X) {
 	if (X <= 2) {
 	return X;
 	} else {
 	return X * fatorial(X - 1);
 	}
}
> fatorial( 5 );
120

Ou:

 

> Binet = function( X ){
... var sqrt5 = Math.sqrt( 5 );
...
... return ( 1 / sqrt5 ) * ( Math.pow( ( 1 + sqrt5 ) / 2 , X ) - Math.pow( ( 1 - sqrt5 ) / 2 , X ) );
... }
function (X) {
 	var sqrt5 = Math.sqrt(5);
 	return 1 / sqrt5 * (Math.pow((1 + sqrt5) / 2, X) - Math.pow((1 - sqrt5) / 2, X));
}
> Fibonacci = function( total ){
... var ret = [];
...
... for ( var i = 0 ; i < 10 ; ++i ) ret.push( Math.round( Binet( i ) ) );
...
... return ret;
... }
function (total) {
 	var ret = [];
 	for (var i = 0; i < 10; ++i) {
 	ret.push(Math.round(Binet(i)));
 	}
 	return ret;
}
> Fibonacci( 10 );
[ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ]
>

Legal, né ?!!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Showww demais isso.

 

Dúvidas.

 

No caso de não possuir um schema pré-definido.

 

Normalmente nas aplicações eu possuo tabelas com informações adicionais (tipos, grupos) que apenas servem para descrever algo (id de grupo de usuários 1=Administrador, 2=Analista, 3=Cliente).

 

No caso de banco de dados NoSQL, eu "guardaria" a informação completa? E se eu precisar listar isso ou alterar? (Ou seja seria a mesma forma de um banco de dados relacional?)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Essa é a pergunta de ouro, Douglas,

 

Temos usuários que podem ser administradores, analistas ou clientes porém, independente do grupo que esse usuário está, ele ainda é um usuário.

 

Vejamos:

 

user = {
	name : "neto",
	pswd : "neto",
	real : "João Neto",
	email : "email@teste.com",
	groups : [ { name : "analista" } ]
}

Como pode ver, o usuário João Neto é um analista mas, e se tivermos algum usuário que, além de analista é também um "administrador" ??

 

Simples:

 

user = {
	name : "douglas",
	pswd : "douglas",
	real : "Douglas",
	email : "email@exemplo.com",
	groups : [ { name : "administrador" } , { name : "analista" } ]
}

Percebe que a informação está contextualizada ?

 

Não precisamos de uma coleção apenas para armazenar esses grupos, eles estão no mesmo contexto dos usuários.

 

Para localizar os administradores do sistema é bem simples:

 

> db.users.find( { "groups.name" : "administrador" } );
{ "_id" : ObjectId("4c72b364a07f1b3129263edb"), "name" : "douglas", "pswd" : "douglas", "real" : "Douglas", "email" : "email@exemplo.com", "groups" : [ { "name" : "administrador" }, { "name" : "analista" } ] }

Ou os analistas:

 

> db.users.find( { "groups.name" : "analista" } ); 	
{ "_id" : ObjectId("4c72b356a07f1b3129263eda"), "name" : "neto", "pswd" : "neto", "real" : "João Neto", "email" : "email@teste.com", "groups" : [ { "name" : "analista" } ] }
{ "_id" : ObjectId("4c72b364a07f1b3129263edb"), "name" : "douglas", "pswd" : "douglas", "real" : "Douglas", "email" : "email@exemplo.com", "groups" : [ { "name" : "administrador" }, { "name" : "analista" } ] }

Perceba que a busca é feita usando a propriedade "groups.name", afinal, queremos localizar todos os documentos que possuam um grupo com propriedade "name" igual a alguma coisa.

 

Se eu tiver conseguido ser claro, dê um toque, que vamos para um exemplo mais avançado, trabalhando com 2 contextos diferentes.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Certo. Entendido. :)

 

Mas como eu faço para ter uma listagem de todos os grupos existentes? Ou seja eu preciso ler todos os registros do banco para ter essa informação?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas como eu faço para ter uma listagem de todos os grupos existentes? Ou seja eu preciso ler todos os registros do banco para ter essa informação?

 

Você utiliza MapReduce:

 

Map

> map = function(){ 	
... this.groups.forEach( function( group ){
... 	emit( group.name , { count : 1 } );
... }
... );
... }
function () {
	this.groups.forEach(function (group) {emit(group.name, {count:1});});
}

Reduce

> reduce = function( k , v ){ 	
... for ( var i = 0 , t = v.length , r = 0 ; i < t ; ++i ){
... 	r += v[ i ].count;
... }
... 
... return { count : r };
... }
function (k, v) {
	for (var i = 0, t = v.length, r = 0; i < t; ++i) {
 	r += v[i].count;
	}
	return {count:r};
}

Executando:

 

> db[ db.users.mapReduce( map , reduce ).result ].find();
{ "_id" : "administrador", "value" : { "count" : 1 } }
{ "_id" : "analista", "value" : { "count" : 2 } }

Opz, temos 2 grupos:

 

administrador, em apenas 1 usuário

analista, em 2 usuários.

 

Um MAP nada mais é que um par CHAVE-VALOR, onde para a chave, usamos o nome do grupo.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Este MapReduce é feito no próprio Mongo? Seria então um "correspondente" ao Stored Procedures dos bancos relacionais? Ou já vem criado por padrão?

 

Carlos Eduardo

Compartilhar este post


Link para o post
Compartilhar em outros sites

Este MapReduce é feito no próprio Mongo?

 

Sim, o MapReduce é nativo do Mongo.

 

Seria então um "correspondente" ao Stored Procedures dos bancos relacionais?

 

Ele seria similar ao GROUP BY do SQL, porém, as funções de MAP e REDUCE são construídas para cada caso específico, você pode ter vários MAPs diferentes para coleções diferentes.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Captei a idéia... Mas aí não temos informação duplicada? Por exemplo, se tiver 200 usuários como administrador, teremos a palavra administrador escrita 200 vezes no banco. Então, para modificar, tenho que modificar os 200 registros, certo? Tipo, se quiser trocar administrador para gestor.

 

Carlos Eduardo

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pessoal,

 

Tópico muito interessante, parabéns pelas postagens, serve como um FAQ para dúvidas gerais do MongoDB!

 

Responde as dúvidas mais comuns de quem ainda não está acostumado com bancos não-relacionais.

 

Abraços

Compartilhar este post


Link para o post
Compartilhar em outros sites

Cuidado com as informações embutidas, elas são MUITO lentas em grandes quantidades. Uma solução é usar o DBREF que linka dois documentos distintos tipo uma chave primária/estrangeira em bancos relacionais.

Compartilhar este post


Link para o post
Compartilhar em outros sites

E na prática? Como que é feita a interação com o PHP, por exemplo (não sei se trabalha com outras linguagens)? Abstração de dados, como que com a PDO, pelo menos nativamente, não existe (ainda).

 

Como que a coisa funciona? Onde que esse banco de dados armazena as informações? Tem melhor performance que MySQL, PostgreeSQL...?

 

É interessante ter um assunto novo, mas ainda faltam muitas informações...

Compartilhar este post


Link para o post
Compartilhar em outros sites

E na prática? Como que é feita a interação com o PHP, por exemplo (não sei se trabalha com outras linguagens)? Abstração de dados, como que com a PDO, pelo menos nativamente, não existe (ainda).

 

Como que a coisa funciona? Onde que esse banco de dados armazena as informações? Tem melhor performance que MySQL, PostgreeSQL...?

 

É interessante ter um assunto novo, mas ainda faltam muitas informações...

Você parece meio "perdido" a respeito de como funciona um sistema de banco de dados 'NoSQL', pesquise no Google e veja qual a 'filosofia' por trás antes de se aventurar no código ;)

Na verdade existem drivers mongodb para várias linguagens e bibliotecas para trabalhar com vários frameworks, como Rails e Sinatra (usando ruby) por exemplo.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Só pareço perdido? :lol:

 

Eu já li um pouquinho a respeito, mas não vi nada na prática. Pelo menos não com PHP, que eu programo ;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eita, nunca tinha visto essa seção do manual.

 

hummmm.

 

No primeiro post deste tópico, eu aponto um link para o post #4 do tópico que fala sobre a instalação do MongoDB e, nesse post #4 eu aponto o link do manual, o link para instalação do Driver para PHP e, de quebra, disponibilizei para download todas as classes comentadas, para completação automática dos métodos no Eclipse.

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bom pessoal temos alguns códigos utilizando PHP + MongoDb no nosqlbr.com.br até um sisteminha básico de tarefas. Sobre um ORM para NOSQL ja existe o Doctrine p/ o Mongo mas ainda não utilizei-o, então não posso opinar. Muito bom artigo João, mandei-te uma msg.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Tive a mesma dúvida do Matias no post #8. No banco de dados relacional, aprendemos a criar tabelas para evitar esse problema. Mas no exemplo acima, ficou parecendo duplicidade de informações.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Tive a mesma dúvida do Matias no post #8. No banco de dados relacional, aprendemos a criar tabelas para evitar esse problema. Mas no exemplo acima, ficou parecendo duplicidade de informações.

 

hargon, é tudo uma questão de modelagem.

 

Vamos supor que tenhamos uma lista (coleção) de usuários, esses usuários terão uma flag que indica qual o seu nível.

 

No caso específico, exposto pelo Douglas:

 

Normalmente nas aplicações eu possuo tabelas com informações adicionais (tipos, grupos) que apenas servem para descrever algo (id de grupo de usuários 1=Administrador, 2=Analista, 3=Cliente).

 

Os grupos não apenas são pré-definidos, pelo que pude entender, eles são rígidos.

 

Precisamos, logo de início, lembrar que por não ter um schema, a responsabilidade das regras dos dados ficam na aplicação.

 

Como eu modelaria, na aplicação, a situação acima:

 

Imagem Postada

 

Como pode ver, pelo fato de ser uma informação não mutável, rígida e aplicação depender desses literais, o grupo de usuários é, na verdade, uma enumeração.

 

Sendo assim:

 

UserGroup.php

<?php
interface UserGroup {
const ADMINISTRATOR = 1;
const ANALYST = 2;
const CLIENT = 4;
}

 

User.php

<?php
class User {
private $_id;
private $group;
private $name;
private $pswd;

/**
 * @var MongoCollection
 */
private $users;

public function __construct( MongoCollection $userCollection , $name , $group , $pswd , MongoId $_id = null ){
	$this->users = $userCollection;
	$this->_id = $_id;
	$this->group = $group;
	$this->name = $name;
	$this->pswd = $pswd;
}

public static function login( MongoCollection $userCollection , $name , $pswd ){
	$userArr = $userCollection->findOne( array(
		'name' => $name,
		'password' => $pswd
	) );

	if ( is_null( $userArr ) ){
		throw new Exception( 'Opz, Dados inválidos.' );
	} elseif ( isset( $userArr[ 'name' ] ) && isset( $userArr[ 'password' ] ) && isset( $userArr[ 'group' ] )){
		$userObj = (object) $userArr;

		return new User( $userCollection , $userObj->name , $userObj->group , $userObj->password , $userObj->_id );
	} else {
		throw new UnexpectedValueException( 'Opz, dados inconsistentes.' );
	}
}

public function getID(){
	return $this->_id;
}

public function getName(){
	return $this->name;
}

public function getGroup(){
	return $this->group;
}
}

 

Supondo que tenhamos gravado a seguinte informação no banco:

 

<?php
$mongo = new Mongo( 'mongodb://meuUsuario:minhaSenha@meuHost:27017' );
$mongo->selectDB( 'meudb' )->selectCollection( 'users' )->save( array( 'name' => 'João Batista Neto' , 'group' => 1 , 'password' => 'minhasenha' ) );

 

Basta o usuário fazer seu login:

 

try {
$mongo = new Mongo( 'mongodb://meuUsuario:minhaSenha@meuHost:27017' );

$user = User::login( $mongo->selectDB( 'meudb' )->selectCollection( 'users' ) , 'João Batista Neto' , 'teste' );

switch ( $user->getGroup() ){
	case UserGroup::ADMINISTRATOR : echo 'Bem vindo, administrador...'; break;
	case UserGroup::ANALYST : echo 'Bem vindo, analista...'; break;
	case UserGroup::CLIENT : echo 'Bem vindo, cliente...'; break;
	default:
		echo 'Opz, não conheço esse grupo.';
}
} catch ( MongoException $m ){
echo 'Opz, problemas com o banco...';
} catch ( Exception $e ){
echo $e->getMessage();
}

 

A saída deverá ser:

 

Bem vindo, administrador...

 

A grande questão é, existirão tantos grupos de usuários a ponto de ser necessário ter uma coleção específica para eles ?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Certo.. Muito bom.

 

Agora surgiu outra dúvida.

 

Tipos de dados. Isso também existe nos bancos NoSQL? (datetime, float, decimal, int)

 

No caso de trabalhar com datas or exemplo, como ele vai saber que é uma data, se não existe schema pré-definido?

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.