Bazy NoSQL WB@NOSQL

Hurtowy import danych do Elasticsearch

Dokumentacja:

The Elasticsearch REST API expects the following JSON structure:

{ "index" : { "_index" : "db", "_type" : "collection", "_id" : "id" } }  action and meta data
... JSON to be imported into Elasticsearch ...

Depending on the usage some fields are optional.

Generujemy „przeplatane” JSON-y

Zwykle dane mamy w pliku ideas.json, po jednym JSON-ie w wierszu, na przykład:

{ "quote": "By dojść do źródła, trzeba płynąć pod prąd.", "tags": ["idea"] }

Aby zaimportować do Elasticsearch dane hurtem, musimy każdy JSON poprzedzić JSONE-em z „action and metadata”, przykładowo takim:

{ "index": { "_type": "lec" } }
{ "quote": "By dojść do źródła, trzeba płynąć pod prąd.", "tags": ["idea"] }

Jak to zrobić? Skorzystamy z narzędzia jq:

cat concepts.json | \
  jq --compact-output '{ "index": { "_type": "lec" } }, .' > concepts.bulk

To samo co powyżej, ale bez cat:

< concepts.js jq --compact-output '{ "index": { "_type": "lec" } }, .' > concepts.bulk

Zanim zapiszemy dane w bazie, usuwamy indeks concepts (dlaczego?):

curl -s -XDELETE localhost:9200/concepts ; echo  # add newline
curl -s -XPOST   localhost:9200/concepts/_bulk --data-binary @concepts.bulk ; echo

CRUD

Delete, delete-lec-ideas.bulk:

{ "delete" : { "_index" : "ideas", "_type" : "lec", "_id" : "1" } }
{ "delete" : { "_index" : "ideas", "_type" : "lec", "_id" : "2" } }

Przykład:

curl -s -XPOST localhost:9200/_bulk --data-binary @delete-lec-ideas.bulk ; echo

Create or ‘put-if-absent’, create-ideas.bulk:

{ "create": { "_index": "ideas", "_type": "lec", "_id": 1 } }
{ "quote": "Czas robi swoje. A ty człowieku?", "tags": ["man", "time"] }
{ "create": { "_index": "ideas", "_type": "lec", "_id": 4 } }
{ "quote": "Bądź realistą: nie mów prawdy.", "tags": ["idea", "truth"] }

Przykład:

curl -s -XPOST localhost:9200/_bulk --data-binary @create-ideas.bulk ; echo

Przykłady

Więcej przykładów umieściłem w katalogu pp/elasticsearch/bulk.

Mapping types are containers for documents, in a similar way to how tables in a relational database are containers for rows. They are often called simply types, because you'd put different types of documents in different mapping types.

Elasticsearch in Action

Mapping types

„You can think of a mapping like a schema in a relational database, because it contains information about each field. For example, «artist» would be a string, while «price» could be an integer.”

Na przykład, poniższe polecenie wypisze typy przypisane automatycznie przez Elasticsearch dokumentom z concepts/lec:

curl -s localhost:9200/concepts/lec/_mapping | jq .

Oto wynik:

{
  "lec": {
    "properties": {
      "tags": {
        "type": "string"
      },
      "quote": {
        "type": "string"
      }
    }
  }
}

Typy pól:

A tak definiujemy swoje „mapping”:

curl -s -XPUT localhost:9200/concepts/lec/_mapping -d '
{
  "lec": {
    "properties": {
      "tags": {
        "type": "string",
        "index": "not_analyzed"
      },
      "quote": {
        "type": "string",
        "analyzer": "snowball"
      }
    }
  }
}'

Oto wynik:

{
  "status": 400,
  "error": "MergeMappingException[Merge failed with failures …"
}

Niestety nie udało się uaktualnić typów. W takiej sytuacji musimy usunąć indes concepts. Następnie toworzymy pusty index w którym zapisujemy powyżej zdefiniowane mapping. Dopiero na końcu zapisujemy dane:

curl -s -XDELETE localhost:9200/concepts
curl -s -XPOST localhost:9200/concepts
curl -s -XPUT localhost:9200/concepts/lec/_mapping -d '
{
  "lec": {
    "properties": {
      "tags": {
        "type": "string",
        "index": "not_analyzed"
      },
      "quote": {
        "type": "string",
        "analyzer": "snowball"
      }
    }
  }
}'
curl localhost:9200/concepts/_bulk --data-binary @concepts.bulk

Sprawdzamy nasze mapping:

curl -s localhost:9200/concepts/lec/_mapping

Oto wynik:

{
  "lec": {
    "properties": {
      "tags": {
        "index_options": "docs",
        "omit_norms": true,
        "index": "not_analyzed",
        "type": "string"
      },
      "quote": {
        "analyzer": "snowball",
        "type": "string"
      }
    }
  }
}

I mamy to o co prosiliśmy. No, prawie!

Przykładowe zapytania, w których korzystamy z Lucene Query Syntax:

curl -s 'localhost:9200/concepts/lec/_search?q=kakao'         | jq '.hits.hits[]'
curl -s 'localhost:9200/concepts/lec/_search?q=quote:kakao'   | jq '.hits.hits[]'
curl -s 'localhost:9200/concepts/lec/_search?q=tags:kakao'    | jq '.hits.hits[]'
curl -s 'localhost:9200/concepts/lec/_search?q=tags:krowie'   | jq '.hits.hits[]'
curl -s 'localhost:9200/concepts/lec/_search?q=quote:chocia*' | jq '.hits.hits[]' # polskie literki… bug?

TODO: Zainstalować i użyć Morfologik (Polish) Analysis Plugin for ElasticSearch