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

piątek, 27 września 2013

[FullTextSearch] ElasticSearch: Query DSL

Zapytania do ElasticSearch wysyłamy REST-owo. Do prostego pobierania dokumentów wystarczą nam sparametryzowane zapytania typu GET. Aby jednak poznać pełnię możliwości ElasticSearch musimy poznać język zapytań DSL, w którym parametry zapytania ustawiamy poprzez załączenie z żądaniem typu POST odpowiedniego obiektu w postaci JSON. ElasticSearch udostępnia wiele rodzajów zapytań. Poniżej ciekawsze z nich.

Term Query

Tego typu zapytania można wykonywać na dwa sposoby. Przy użyciu czasownika GET

http://localhost:9200/books/book/_search?q=title:parallel&pretty=true

lub za pomocą czasownika POST

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 59

{
 "query" : {
  "term" : { "title" : "parallel" }
 }
}

W obu przypadkach otrzymujemy ten sam rezultat

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 809

{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 0.095891505,
    "hits" : [ {
      "_index" : "books",
      "_type" : "book",
      "_id" : "1",
      "_score" : 0.095891505, "_source" : {
 "title": "Parallel Worlds: A Journey Through Creation, Higher Dimensions, and the Future of the Cosmos",
 "author": "Michio Kaku",
 "year": 2006,
 "price": 12.06
}
    }, {
      "_index" : "books",
      "_type" : "book",
      "_id" : "3",
      "_score" : 0.095891505, "_source" : {
 "title": "Hyperspace: A Scientific Odyssey Through Parallel Universes, Time Warps, and the Tenth Dimension",
 "author": "Michio Kaku",
 "year": 1995,
 "price": 16.36
}
    } ]
  }
}

Term Query to takie zapytanie, które zwraca wyniki tylko wtedy, gdy podamy dokładnie słowo, które znajduje się w danym polu. Nie zadziałają tutaj zapytania przedrostowe typu "paral*". Wyniki za każdym razem dostępne będą w tablicy dostępnej pod hits.hits. Pełny dokument zwracany jest w polu _source. Pobieranie tak zagnieżdżonych informacji wydaje się niewygodne, a czasami także niepotrzebnie przesyłany jest cały duży dokument, dlatego możemy podać, które pola nas interesują.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 92

{
 "fields" : ["title","author"],
 "query" : {
  "term" : { "title" : "parallel" }
 }
}

W rezultacie otrzymamy:

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 797

{
  "took" : 9,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 0.095891505,
    "hits" : [ {
      "_index" : "books",
      "_type" : "book",
      "_id" : "1",
      "_score" : 0.095891505,
      "fields" : {
        "title" : "Parallel Worlds: A Journey Through Creation, Higher Dimensions, and the Future of the Cosmos",
        "author" : "Michio Kaku"
      }
    }, {
      "_index" : "books",
      "_type" : "book",
      "_id" : "3",
      "_score" : 0.095891505,
      "fields" : {
        "title" : "Hyperspace: A Scientific Odyssey Through Parallel Universes, Time Warps, and the Tenth Dimension",
        "author" : "Michio Kaku"
      }
    } ]
  }
}

Odpytywać można także tylko indeks (bez podawania typu book) oraz wiele indeksów lub wiele typów. Na przykład:

http://localhost:9200/books,books5/book/_search?q=author:kaku&pretty=true

Terms Query

Podajemy kilka wartości, które nie będą analizowane (muszą to być dokładne wartości), oraz opcjonalnie ile z nich musi pasować, aby zwrócony został dany dokument.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 111

{
 "query" : {
  "terms" : { "title" : ["parallel","hyperspace","worlds"],
  "minimum_match" : 2
  }
 }
}

Match Query

W tym zapytaniu podane wyrażenie zostaje przetworzone przez analizator. A zatem tym razem możemy podawać niepełne wyrazy, na przykład:

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 65

{
 "query" : {
  "match" : { "title" :"para worlds"
  }
 }
}

Zapytanie takie zwróci wyniki. Domyślnie słowa są łączone operatorem OR, można to zmienić w następujący sposób.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 122

{
 "query" : {
  "match" : {
   "title" : {
    "query" : "parallel worlds",
    "operator" : "and"
   }
  }
 }
}

Match Phrase

Jeżeli chcemy wyszukać frazy składającej się z większej ilości słów, korzystamy z match_phrase, gdzie parametr slop definiuje, ile nieznanych słów może się mieścić pomiędzy podanymi wyrazami.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 121

{
 "query" : {
  "match_phrase" : {
   "title" : {
    "query" : "parallel worlds",
    "slop" : 1
   }
  }
 }
}

Multi Match

Efekt zapytania typu "Match" możemy uzyskać także na wielu polach dokumentu.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 131

{
 "query" : { 
  "multi_match" : {
   "query" : "univers michio penrose",
   "fields" : [ "title", "author" ]
  }
 }
}

Identifiers Query

Kiedy dokładnie znamy identyfikatory dokumentów, które chcemy pobrać, korzystamy z tego typu zapytania.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 73

{
 "query" : {
  "ids" : {
   "values" : [ "1", "4", "5" ]
  }
 }
}

Prefix Query

Wyszukiwanie po przedrostkach.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 63

{
 "query" : {
  "prefix" : {
   "title" : "uni"
  }
 }
}

Fuzzy Like This Query

Zapytanie, które dopuszcza literówki w podanej frazie.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 115

{
 "query" : {
  "fuzzy_like_this" : {
   "fields" : ["title", "author"],
   "like_text" : "penrse"
  }
 }
}

Range Query

Podajemy zakresy, przydatne dla wartości numerycznych.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 100

{
 "query" : {
  "range" : {
   "year" : {
    "from" : 1990,
    "to" : 2000
   }
  }
 }
}

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);
}