Pokazywanie postów oznaczonych etykietą mongodb. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą mongodb. Pokaż wszystkie posty

niedziela, 29 września 2013

[FullTextSearch] ElasticSearch: river MongoDB

Najpopularniejszym sposobem dodawania danych do indeksów ElasticSearch jest wysyłanie odpowiednich REST-owych wiadomości. Ale to nie jedyny sposób. Możemy czasem pobierać dane także bezpośrednio z zewnętrznych źródeł, na przykład z baz danych. Mechanizmem, który umożliwia synchronizację pomiędzy źródłem danych, a indeksem ElasticSearch jest river. Rivery są dostępne dla różnych baz NoSql, przykładowo dla MongoDB musimy zainstalować ten river. Aby uruchomić synchronizację pomiędzy instancją bazy, a indeksem ElasticSearch musimy wykonać poniższe kroki. Rozpoczynamy od instalacji pluginu do ElasticSearch. Wchodzimy do katalogu bin w ElasticSearch i wpisujemy polecenie

plugin.bat -i com.github.richardwilly98.elasticsearch/elasticsearch-river-mongodb/1.7.0

Warto pamiętać, że przy instalacji rivera muszą się zgadzać wersje ES i MongoDB. Kompatybilność możemy sprawdzić tutaj.

Teraz musimy przygotować bazę do pracy w trybie replikacji. Plugin river synchronizuje dane dzięki plikowi oplog.rs, udostępnianemu przez MongoDB tylko w trybie replikacji. Plik ten zawiera log z wszystkich zmian, jakie były wykonywane na danych. Pierwotnie jego celem było synchronizowanie danych pomiędzy wieloma replikami, ale wykorzystuje go także river. Do celów ElasticSearch można włączyć replikację składającą się z jednej repliki.

1. Uruchamiamy jeden proces, na którym chodzi baza poleceniem mongod --dbpath rsdata --replSet rs0
2. W drugim oknie wchodzimy do narzędzia konfiguracji poleceniem mongo
3. Wpisujemy polecenie rs.initiate() i czekamy, aż nasz node ustawi się jako PRIMARY (naciśnięcie enter po kilkunastu sekundach powinno upewnić nas o zmianie stanu)

Teraz uruchamiamy ElasticSearch pozostawiając MongoDB działające w innym oknie, tryb konfiguracji możemy opuścić.Wysyłamy PUT z konfiguracją mapowania kolekcji w bazie na indeks w ElasticSearch.

PUT http://localhost:9200/_river/mongolink/_meta HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 214

{
  "type" : "mongodb",
  "mongodb" : {
   "servers" : [
    { "host" : "localhost", "port" : 27017 }
   ],
   "db" : "esbook",
   "collection" : "products"
  },
  "index" : {
   "name" : "esbook"
  }
}

Na indeksie _river stworzyliśmy typ mongolink, który będzie się mapował na kolekcję products z bazy esbook . Teraz możemy dodać dane, na przykład z konsoli mongo.

rs0:PRIMARY> use esbook
switched to db esbook
rs0:PRIMARY> db.products.insert({ "name" : "book", "value" : 200 });

Teraz możemy obejrzeć zmapowane zasoby pod adresem http://localhost:9200/esbook/_search?pretty

niedziela, 24 marca 2013

[NoSQL] MongoDB: Queries

Bazy NoSQL składują dane w postaci dokumentów JSON. Dokumenty takie odpytuje się przy użyciu zapytań specyficznych dla danej bazy danych. MongoDB udostępnia całkiem bogate API do efektywnego przeszukiwania dokumentów składowanych w kolekcjach. Zależnie od języka programowania z którego łączymy się z bazą, zapytania mogą być budowane w nieco odmienny sposób, ale koncepcja pozostaje ta sama. Poniżej kilka użytecznych przykładów na to jak pisać zapytania. Przykłady te nadają się do urchomienia z shella MongoDB (program mongo.exe), jednak na przykład w C# składnia jest bardzo podobna. Tym co różni connectory jest sposób rzutowania danych do danego języka programowania.

Podstawowymi funkcjami służącymi do budowania zapytań są find i findOne. Pierwsza zwraca wszystkie dokumenty spełniające dany warunek, natomiast findOne zwraca tylko pierwszy dokument spełniający ten warunek. Jako argument przekazuje się obiekt JavaScript zgodny z językiem zapytań. Dokumentacja MongoDB mówi, że find jest odpowiednikiem instrukcji SELECT z SQL, natomiast obiekt przekazywany jako parametr odpowiada instrukcji WHERE.

Wybieranie bezwarunkowe:

Nie podając warunku jako argument dla obu funkcji wybieramy wszystkie dokumenty, bądź pierwszy zaindeksowany.

db.users.find();
db.users.findOne();

Przeszukiwanie warunkowe:

Tutaj mamy sporo możliwości. Możemy żądać by dane property miało dokładnie jakąś wartość:

db.users.find({lastname: "Doe"});

Przeszukiwanie warunkowe wykonuje się za pomocą operatorów $lt i $gt (względnie $lte czy $gte).

var query = {};
query.lastname = "Doe";
query.age = { $gt :20};
db.users.find(query);

Podanie dwóch warunków powoduje, że oba muszą być spełnione, aby dokument został zwrócony.

Dokumenty zagnieżdżone:

Przy przeszukiwaniu zagnieżdżonych dokumentów, stosuje się notację z kropkami, tak jakbyśmy odczytywali zagnieżdżone property w JavaScripcie:

var query = {};
query["address.country"] = "USA";
db.users.find(query);

Projekcje:

Drugim, opcjonalnym argumentem dla zapytań jest obiekt, w którym możemy zadecydować, które pola nas interesują. Domyślnie zwrócone zostaną wszystkie pola dokumentu. Jeżeli chcemy któreś konkretne pola, pomijając inne, oznaczamy te pożądane wartością 1. Przy oznaczeniu pierwszego pola przez 1 zostanie zwrócone ono, oraz pola _id nadawane przez bazę. Aby je wyłączyć, możemy ustawić tą wartość na 0. Zatem aby zwrócić dokumenty z samymi imionami wystarczy:

db.users.find(query, {firstname:1, _id: 0});

Paginacja:

Stronnicowanie może być wykonywane bezpośrednio na bazie danych przy użyciu operatorów limit i take. 

db.users.find().limit(1).skip(1);

Sortowanie:
.
Podobnie jak paginacja wykonywane na wynikach jako kolejna funkcja w łańcuchu wywołań

db.users.find().sort({age: 1});

Wartość 1 oznacza sortowanie rosnąco, wartość -1 sortowanie malejąco.

środa, 20 marca 2013

[NoSQL] MongoDB: klient C#

MongoDB, jako jedna z najpopularniejszych, jeżeli nie najpopularniejsza baza NoSQL, dostarcza drivery dla większości najpopularniejszych współcześnie języków programowania. Firma 10gen udostępnia oficjalny sterownik dla języka C#, który można zainstalować w projekcie przy użyciu menadżera pakietów NuGet.


API jest bardzo proste, poniżej pokazane proste CRUDowe operacje:

Dodawanie do kolekcji:

public static void Insert()
{
    var settings = new MongoServerSettings();
    settings.Server = new MongoServerAddress("127.0.0.1", 27017);
    var server = new MongoServer(settings);

    var collection = 
        server.GetDatabase("mongotest").GetCollection("users");

    var doc = new BsonDocument();
    doc["firsname"] = "John";
    doc["lastname"] = "Doe";
    doc["age"] = 17;
    collection.Insert(doc);
}

Najprostszym sposobem dodawania dokumentów do kolekcji jest klasa BsonDocument. Klasa ta jest odpowiednikiem JSONa. BsonDocument może składać się z par klucz - wartość, gdzie wartością może być kolejny BsonDocument, tablica (BsonArray) czy zwykły obiekt typu prostego. Na powyższym przykładzie widać też, jak niewiele kodu potrzeba by składować dane, serwer MongoDB na początku nie zna ani bazy mongotest ani też tabeli users - zostaną one utworzone.

Odczyt i aktualizację danych można wykonać w następujący sposób:

public static void Modify()
{
    var settings = new MongoServerSettings();
    settings.Server = new MongoServerAddress("127.0.0.1", 27017);
    var server = new MongoServer(settings);

    var collection =
        server.GetDatabase("mongotest").GetCollection("users");

    BsonDocument jdoe = 
        collection.FindOne(Query.GT("age", BsonValue.Create(15)));

    jdoe["age"] = 19;
    collection.Save(jdoe);
}

Metoda FindOne gwarantuje zwrócenie pierwszego obiektu spełniającego dany warunek, natomiast klasa Query udostępnia szereg statycznych metod, umożliwiających budowanie zapytań MongoDB podobnych do tych z shella.

Do usuwania danych można użyć funkcji Remove do której podajemy jako parametr obiekty typu BsonDocument, lub FindAndRemove, która usuwa wszystkie dokumenty spełniające dany warunek.

public static void Delete()
{
    var settings = new MongoServerSettings();
    settings.Server = new MongoServerAddress("127.0.0.1", 27017);
    var server = new MongoServer(settings);

    var collection =
        server.GetDatabase("mongotest").GetCollection("users");

    collection.FindAndRemove(Query.GT("age", BsonValue.Create(15)), 
        SortBy.Null);
}

niedziela, 17 marca 2013

[NoSQL] MongoDB + Node.js: mongoose

Interesującą konfiguracją dla aplikacji webowych jest Node.js jako serwer http udostępniający RESTowe API oraz MongoDB jako baza danych. Za takim pomysłem przemawia przede wszystkim format danych: klient przysyła JSONa, Node.js automatycznie parsuje go do obiektu JavaScript na którym można wykonać pewne operacje a później zapisać w bazie jako JSON znowu.

Klientem MongoDB w Node.js jest mongoose. Można go pobrać z menadżera pakietów poleceniem

npm install mongoose

Po dodaniu pakietu do serwera Node.js przy użyciu konstrukcji require, można rozpocząć pracę z MongoDB. Pakiet mongoose do CRUDowych operacji wykorzystuje obiekty typu Schema. Definiując taki obiekt podajemy jakiego typu pola znajdować się będą w naszych dokumentach. Dzięki temu, wstawiając dokument do bazy mamy pewność, że wstawione zostaną jedynie te pola, które zdefiniowaliśmy w schemacie. Mechanizm ten jest konfigurowalny poprzez opcję strict. Jeżeli ustawimy ją na false, to do bazy trafiać będą całe dokumenty. Jeden schemat mapowany będzie na jedną kolekcję w bazie przy zachowaniu zasad języka angielskiego. Na przykład schemat Car zmapowany zostanie do kolekcji cars. Poniżej prosta konfiguracja:
var mongoose = require('mongoose');
mongoose.connect('localhost','mongoosetest');

var schema = mongoose.Schema({ 
    make: 'string',
    enginePower : 'number'
 }, {strict: false});

var Car = mongoose.model('Car', schema);

Dodawanie dokumentów do bazy przy odbieraniu wiadomości typu POST:
app.post('/store/', function(req, resp) {
    var nm = req.body.name;
    var pw = req.body.enginePower;
    var car = new Car({ make: nm, enginePower: pw, day: "17-03" });
    car.save(function(err){
        if(err){
            resp.send({
                ok: 'sth went wrong'
            });
        }
        else{
            resp.send({
                ok: 'everything is fine'
            });
        }       
    });    
});

Analogicznie pobieranie danych wykonujemy także na obiekcie modelu;
app.get('/store/:key', function(req, resp) {
    Car.find({make: req.params.key}, function(err, docs){
        if(err)
            resp.send(501);
        else
            resp.send(docs);
    })
});

Funkcja find może przekazywać bardziej skomplikowane zapytania MongoDB, te same, które dostępne są w shellu bazy (program mongo.exe).

Z kolei aby usunąć obiekt, używamy funkcji remove wołanej również na modelu. Jako pierwszy parametr podaje się warunek.
app.del('/store/:key', function(req, resp) {
    Car.remove({make: req.params.key}, function(err, docs){
        if(err)
            resp.send(500);
        else
            resp.send(200);
    });
});

czwartek, 14 marca 2013

[NoSQL] MongoDB: Wprowadzenie

Jedną z najpopularniejszych baz NoSQL jest MongoDB. Podobnie jak RavenDB opisany w poprzednich postach jest to baza dokumentowa. Z poziomu API dane są składowane jako dokumenty JSONowe, jednak fizycznie na dysku składowanie odbywa się w postaci binarnych JSONów nazywanych BSON. Każdy dokument otrzymuje od bazy własne unikatowe id.

Kilka zalet i ciekawych funkcjonalności:
  • możliwość tworzenia indeksów na dowolnych polach dokumentów
  • mechanizm automatycznej replikacji danych
  • sharding - balansowanie obciążenia na wiele komputerów
  • bogaty język zapytań
  • oficjalne connectory dla wielu popularnych języków programowania 
Aby rozpocząć pracę z MongoDB należy pobrać pliki ze strony producenta odpowiednią wersję. W folderze należy utworzyć folder, w którym przechowywane będą dane, np. mydata. Serwis mongodb uruchamiany jest z command line, po wejściu do folderu z pobranymi plikami, poleceniem:
mongod --rest --port 27017 --dbpath mydata

Konfiguracja serwera bazy odbywa się również przez command line. Należy wykorzystać polecenie mongo podając numer portu, np.
mongo  --port 27017

Z poziomu shella można wykonywać proste komendy w języku JavaScript, np. zapytania.