Bazy NoSQL WB@NOSQL

ElasticSearch & Ruby

[Daniel Kahneman]

The hallo effect helps keep explanatory narratives simple and coherent by exaggerating the consistency of evaluations: good people do only good things and bad people are all bad.

— Daniel Kahneman

Do eksperymentów poniżej użyjemy rzeczywistych danych. Będziemy zbierać statusy z Twittera. Nie będziemy zbierać ich „jak leci”, tylko te które zawierają interesujące nas słowa kluczowe.

Uwaga: Jeśli wybrane słowo kluczowe jest wpisywane w wielu tweetach, np. wow, to zostaniemy zalani statusami – wow!

Do filtrowania statusów skorzystamy z Streaming APIs:

Streaming API Twittera wymaga uwierzytelnienia. Będziemy potrzebować kluczy i tokenów. Zanjdziemy je na stronie Twitter Apps, którą powinniśmy wcześniej utworzyć.

JSON-y z tweetami (statusy) zawierają wiele pól i tylko kilka z nich zawiera dane nas interesujące. Można się o tym przekonać ogladając na konsoli dowolny status (zawierają one informacje o profile_sidebar_fill_color, profile_use_background_image, itp.).

Dlatego, przed wypisaniem statusu na ekran, powinniśmy go „oczyścić” ze zbędnych rzeczy. Zrobimy to za pomocą skryptu w Ruby.

Access Rate Limiting

Each account may create only one standing connection to the Streaming API. Subsequent connections from the same account may cause previously established connections to be disconnected. Excessive connection attempts, regardless of success, will result in an automatic ban of the client's IP address. Continually failing connections will result in your IP address being blacklisted from all Twitter access.

…more on rate limiting

Zaczniemy od skryptu działającego podobnie do polecenia z curl:

Wymagane klucze i tokeny wpisujemy w pliku YAML z credentials wg schematu:

twitter-credentials.yml
login: LLLLLL
password: PPPPPP
consumer_key: AAAAAAAAAAAAAAAAAAAAA
consumer_secret: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
access_token: CCCCCCCC-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
access_token_secret: DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD

Skrypt uruchamiamy na konsoli w następujący sposób:

ruby fetch-tweets-simple.rb ~/twitter_credentials.yml

[Twitter -> ElasticSearch]

Twitter Stream ⟿ ElasticSearch

Do pobierania statusów i zapisywania ich w Elasticsearch wykorzystamy skrypt

Co to jest mapping?

Mapping is the process of defining how a document should be mapped to the Search Engine, including its searchable characteristics such as which fields are searchable and if/how they are tokenized.

Przed zapisaniem w bazie JSON-a ze statusem, skrypt usuwa z niego niepotrzebne nam pola i spłaszcza jego strukturę.

Zanim zaczniemy zapisywać statusy w bazie, zdefinujemy i zapiszemy w bazie ElasticSearch mapping dla statusów. W tym celu użyjemy skryptu create_tweets_mapping.rb.

Użyjemy tego mappingu.

create_tweets_mapping.rb
{
  mappings: {
    statuses: {
      properties: {
        created_at: { type: 'date', format: 'yyyy-MM-dd HH:mm:ss ZZ' },
        hashtags: { type: 'keyword' },
        text: { type: 'text', analyzer: 'english' }
      }
    }
  }
}

Tworzymy index tweets, uruchamiamy skrypt create-index.rb, (po chwili) sprawdzamy czy mapping zostało zapisane w Elasticsearch. Jeśli nie było błędów, to uruchamiamy skrypt fetch-tweets.rb.

curl -X DELETE localhost:9200/tweets

ruby create_tweets_mapping.rb
curl 'http://localhost:9200/tweets/_mapping?pretty'

ruby fetch-tweets.rb ~/twitter_credentials.yml

Czekamy aż kilka statusów zostanie zapisanych w bazie i wykonujemy na konsoli kilka prostych zapytań:

curl -s 'localhost:9200/tweets/_count'
curl -s 'localhost:9200/tweets/_search?q=authentic&size=1&pretty'
curl -s 'localhost:9200/tweets/_search?size=1&pretty'
curl -s 'localhost:9200/tweets/_search?from=10&size=1&pretty'

Range query (skrót na current time to "now").

curl -s -X GET localhost:9200/tweets/_search -d '{
  "from" : 0, "size" : 20,
  "sort" : [ {"created_at": {"order": "desc"}} ],
  "query": {
    "range" : {
      "created_at" : {
        "gte": "2017-03-08 12:00:00 +0000",
        "lte": "now"
      }
    }
  }
}' | jq .hits.hits[]

W repozytorium tweets-elasticsearch jest prosta SPA do wyszukwania tweetów w tweets/statuses.

Ściąga z obiektu Date (JavaScript)

Inicjalizacja:

new Date();
new Date(milliseconds);
new Date(dateString);
new Date(year, month, day, hours, minutes, seconds, milliseconds);
// parsing
ms = Date.parse('2011-01-31T12:00:00.016');
new Date(ms); // Mon, 31 Jan 2011 12:00:00 GMT

Metody:

d = new Date(1332288000000);
d.getTime();         // 1332288000000
d.getFullYear();     // 2011
d.getMonth();        //    0    (0-11)
d.getDate();         //   31    zwraca dzień miesiąca!
d.getHours();
d.getMinutes();
d.getSeconds();
d.getMilliseconds();

Konwersja na napis:

d.toString();        // 'Mon Jan 31 2011 01:00:00 GMT+0100 (CET)'
d.toLocaleString();  // 'Wed Mar 21 2012 01:00:00 GMT+0100 (CET)'
d.toGMTString();     // 'Wed, 21 Mar 2012 00:00:00 GMT'

Zobacz też Epoch & Unix Timestamp Conversion Tools.