Elasticsearch podstawowe operacje

Co mogę zrobić z Elasticsearch?

Zakładam, że wiesz, czym jest Elasticsearch:
https://jaroslawstadnicki.pl/2024/03/12/co-to-jest-elasticsearch/
Oraz że wiesz, jak go uruchomić:
https://jaroslawstadnicki.pl/2024/03/04/elasticsearch-i-docker/

Jak zacząć z tym pracować?

Zacznijmy od sprawdzenia, czy działa ping! Taki hello world dla Elasticsearch:

GET: http://localhost:9200

❗❗❗ W zależności od konfiguracji używaj http lub https

Jeśli świat nie zechce inaczej, to w wyniku powinno pokazać się
You know, for search

{
“name”: “22e9589dd825”,
“cluster_name”: “docker-cluster”,
“cluster_uuid”: “jUlehFZgRO-ibt6m5VKx6Q”,
“version”: {
“number”: “8.12.0”,
“build_flavor”: “default”,
“build_type”: “docker”,
“build_hash”: “1665f706fd9354802c02146c1e6b5c0fbcddfbc9”,
“build_date”: “2024-01-11T10:05:27.953830042Z”,
“build_snapshot”: false,
“lucene_version”: “9.9.1”,
“minimum_wire_compatibility_version”: “7.17.0”,
“minimum_index_compatibility_version”: “7.0.0”
},
“tagline”: “You Know, for Search
}

Gdzie baza?

Skoro to działa, to co mogę dalej? Gdzie bazy danych? Albo indeksy? Skoro to Elasticsearch;

GET: http://localhost:9200/_cat/indices?v

U mnie pojawia się taki pusty zapis:

health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size

Bo specjalnie dla Ciebie, usunąłem swój indeks i pokazuje co i jak wygląda na początku świata. Nie mam bazy danych i jak z tym żyć? Prościzna, wystarczy jeden strzał i już będzie:

PUT: http://localhost:9200/courses

(Tak, uwaga, tutaj jest PUT, sprawdź sam co odpowie Elasticsearch gdy zaproponujesz POSTa), w poprawnej odpowiedzi otrzymuje potwierdzenie:

{
    “acknowledged”: true,
    “shards_acknowledged”: true,
    “index”: “courses
}
Następnie ponownie zrobię pobranie indeksów:
GET: http://localhost:9200/_cat/indices?v
Będzie skutkowało teraz taką informacją:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size
yellow open courses _JyXbuNdSlqPymuUQDL5tg 1 1 0 0 227b 227b 227b
Polecam robić to u lokalnie, formatowanie nie jest przyjazne dla długich linijek.
Usunięcie bazy, to prosty strzał:
DEL: http://localhost:9200/courses z odpowiedzią
Wynik:
{
    “acknowledged”: true
}
Odtworzę (PUT: http://localhost:9200/courses) indeks i mogę działać
ponownie.

Gdzie dane

Mam miejsce, gdzie mogę trzymać dane, to jak je tam teraz załadować? Czy to proste? Jak pisanie tego posta!
Wystarczy wykonać
POST: http://localhost:9200/courses/_doc

U mnie body wygląda w ten sposób:

{
    “name“: “elasticsearch i docker”,
    “description“: “Elasticsearch Jest to pierwszy z cyklu wpisów o Elasticsearch, opracowane na podstawie oficjalnej dokumentacji dostępnej pod linkiem: https://www.elastic.co/guide/en/elasticsearch/reference/8.12/docker.html W prywatnym projekcie, nad którym pracuje, do wyszukiwania kursów będę korzystać  narzędzia Elasticsearch. Ponieważ potrzebuje lepiej poznać to narzędzie, postanowiłem udokumentować kilka kroków z mojej podróży, aby dzielić się wiedzą, a jednocześnie liczę, że przyda się to także innym. Zamierzam korzystać z instalacji opartej o Docker i poniży wpis jest właśnie taką uproszczoną instrukcją jak tego dokonać.”
}

Dla lepszego efektu wykonam jeszcze jedną operację z takim body na ten sam url jak poprzednio:

{
    “name“: “co to jest elasticsearch”,
    “description“: “Czym jest elasticsearch? Sam elastisearch reklamuje się tym sloganem: You know, for search (and analysis) I generalnie tak jest, gdy myślę o szukaniu danych (wydajnym) to na myśl przychodzi mi właśnie Elasticsearch. Ale czekaj, bo to nie tylko szukanie – to znaczy i tak i nie, bo Elastic to coś więcej niż search, jest tam także Kibana, która ułatwia przeglądanie tego co mamy w bazie czy Logstash do wrzucania logów do tejże bazy. Ja, póki co skupiam się na Elasticsearch jako silniku do szukania, oprócz szukania potrafi też podać dodatkowe informacje dotyczące wyników; agregacje, statystyki, czy wyniki bliskie do tych, które są dla nas interesujące..”
}
Wyniki takich operacji wyglądają jakoś tak:
{
    “_index”: “courses”,
    “_id”: “QOTVb44BFw2yxe2y3POW”,
    “_version”: 1,
    “result”: “created”,
    “_shards”: {
        “total”: 2,
        “successful”: 1,
        “failed”: 0
    },
    “_seq_no”: 0,
    “_primary_term”: 1
}
Sprawdzam, czy są:
GET: http://localhost:9200/courses/_count
Czary:
{
    “count”: 2,
    “_shards”: {
        “total”: 1,
        “successful”: 1,
        “skipped”: 0,
        “failed”: 0
    }
}

Poka dane

Wygląda, że się zgadza! , gdzie szukanie? Selekt gwiazdka from students!
GET: http://localhost:9200/courses/_search
Muszę się ratować gistem, także:

Taki search “szuka” wszystkiego, co jest, można to ograniczać i zawężać wyniki, np:

http://localhost:9200/courses/_search?size=1&from=1

Ale gdyby tak poszukać czegoś konkretnego, warto wrzucić coś do body takiego zapytania, także robimy:
GET: http://localhost:9200/courses/_search

Z body ustawionym w taki sposób:

{
    “query”: {
        “simple_query_string”: {
            “query”: “co to jest elasticsearch”
        }
    }
}

Zwróci oba dokumenty, gdzie ten z takim właśnie tytułem będzie jako pierwszy, natomiast podmiana tego query na “elasticsearch i docker” – zmieni kolejnośc wyników – zwróć uwagę na pola:”max_score“: 3.9204745, oraz “_score“: 3.9204745 czy “_score“: 1.0661358. Warto także wrzucić w query to, co jest w opisie i zobaczyć czy szukanie też będzie działać – 🧝‍♂️

WHERE

Gdybyś chciał zawęzić pole wyszukiwania tylko do np. nazwy posta, można to zrobić modyfikując body w taki sposób:

{
    “query”: {
        “simple_query_string”: {
            “query”: “docker”,
            “fields“:[“name”]
        }
    }
}
Co spowoduje, że szukać będzie w name i nie zwróci już obu wyników.

Group by

Na koniec jeszcze grupowanie, bo to jest bardzo przydatna opcja. Aby lepiej pokazać wyniki dodałem, jeden dokument z takim samym tytułem — i jakimś krótkim opisem, a dodatkowo wyłączyłem szukanie, na potrzeby przejrzystego wyniku agregacji:

GET: http://localhost:9200/courses/_search

Body:
{
    “size”:0,
    “aggs”:{
        “names”:{
            “terms”:{
                “field”:”name.keyword”,
                “size”:10
            }
        }
    }
}
Rezultat:
Wynik zawiera informacje o agregowaniu na podstawie nazwy — co daje dwa dokumenty dla “elasticsearch i docker” (jeden, specjalnie dodany) oraz jeden dla “co to jest elasticsearch”.
Jeśli włączyć do tego wyszukiwanie, to agregowanie danych zostanie wykonane tylko na wynikach pasujących do zapytania:
{
    “size“:0,
    “query“: {
        “simple_query_string”: {
            “query”: “docker”,
            “fields”:[“name”]
        }
    },
    “aggs”:{
        “names”:{
            “terms”:{
                “field”:”name.keyword”,
                “size”:10
            }
        }
    }
}
W wypadku w agregacji dostanę tylko informacje o pasujących dokumentach, ale sam dokument nie zostanie wyświetlony — ponownie użyłem “size:0″.

Optymalizacja i selekcja danych

Na koniec jeszcze optymalizacja wyników, nie trzeba / nie powinno się ciągnąć pełnego dokumentu, dlatego można zawęzić się tylko do interesujących pól:
{
    “query”: {
        “simple_query_string”: {
            “query”: “docker”,
            “fields”:[“name”]
        }
    },
    “fields”:[“name”],
    “_source”:false
}
Zwróć uwagę na dodatkowe pole _source – sprawdź różnice w wynikach gdy usuniesz ten atrybut z body zapytania.
Od teraz potrafisz, tworzyć i usuwać bazy w elasticsearch, dodawać oraz szukać danych w swojej bazie opartej o Elasticsearch

co to jest elasticsearch

Czym jest elasticsearch?

Sam elastisearch reklamuje się tym sloganem:

You know, for search (and analysis)

I generalnie tak jest, gdy myślę o szukaniu danych (wydajnym) to na myśl przychodzi mi właśnie Elasticsearch. Ale czekaj, bo to nie tylko szukanie – to znaczy i tak i nie, bo Elastic to coś więcej niż search, jest tam także Kibana, która ułatwia przeglądanie tego co mamy w bazie czy Logstash do wrzucania logów do tejże bazy.
Ja, póki co skupiam się na Elasticsearch jako silniku do szukania, oprócz szukania potrafi też podać dodatkowe informacje dotyczące wyników; agregacje, statystyki, czy wyniki bliskie do tych, które są dla nas interesujące.

Continue reading

elasticsearch i docker

Update, Attach, SaveChanges i inne składniki EntityFrameworka

Wprowadzenie

Pracując z Entity Framework możemy doceniać jego moc oraz zapomnieć o tym, jaką potężną magią on operuje. Np. skąd ten diabeł wie, co się zmieniło, a kiedy muszę mu o tym przypomnieć.

Mam prostą regułę w głowie, jeśli wyciągam dane z kontekstu i zaraz od razu wrzucam je tam z powrotem, nie ustawiam do tego flag, nie wtrącam się, nie robię hokus-pokus i zakładam, że wszystko będzie dobrze.

Jeśli wyciągam dane na dłużej, wtedy podpowiadam EF, żeby nie śledził zmian, bo to trochę zajmie, zanim te dane wrócą do bazy danych i szkoda zachodu.

Dane można zapisać / zaktualizować na kilka sposobów:

Na raz:

  • Pobierz
  • Zmodyfikuj
  • Zapisz

Na dwa

  • Utwórz
  • Przypnij
  • Zapisz

Na trzy:

  • Nie zaglądam, jeśli działa u Ciebie to dawaj dawaj na produkcje 😉

Wygląda że proste? To sprawdzam, co i jak zadziała

W obu przypadkach mam taki oto kod:

Nie oszukujmy się, bunkrów nie ma, kontekst oraz jakiś POCO na potrzeby przykładów.

Uno

Ten przykład działa, pobieram, modyfikuje, zapisuje. EF robi magię i potrafi sam wykryć zmianę w encji – nie wymaga wywołania `update` aby znaleźć i nanieść zmiany na obiekcie. PFM! Wymagany jest natomiast strzał do bazy aby pobrać początkowy obiekt do modyfikacji.
Na plus – nie tracę informacji, który nie modyfikuje. Wrócę do tego niżej.

Duo(s)

Teraz się zacznie, co się stanie, gdy wyciągnę dane bez śledzenia i zapiszę zamiany:

Pudło! Nic, EF cichutko “zapisał” zmienione encje, ale mój odcięty obiekt nie był na tej liście, w związku z tym – nic się nie wydarzyło. Może tylko odrobina smutku. Ale sam tego chciałem, przecież mówiłem – bez śledzenia!

Spróbuje inaczej, stworze nowy obiekt, przypnę go do kontekstu i wtedy zapisze:

Hus! (Dzięki panie Łukaszu) – nie działa! Ale czemu? Przecież obiekt jest w kontekście!

Ach, obiekt został dodany, ale jest oznaczony jako unchanged! Ok-sprawdzam:

Czyli co, nawet jeśli mam nowy obiekt i przypiąłem go do kontekstu nadal nie jest śledzony? Nie koniecznie, obiekt został przypięty i jest śledzony, ale zmiany nie nastąpiły po włączeniu śledzenia. Natomiast taki kod:

Robi już robotę, zwróć uwagę, że zmiany następują po przypięci i włączeniu śledzenia zmian

Zamiast takiego cyrkolenia się, można iść na skróty i wywołać po prostu Update:

Który ustawia status obiektu na modified i zapisuje zmiany.

Turbo mega uwaga:
Oba przypadku zerują wartość dla ReleaseDate który dostanie domyślną 01.01.0001 00:00:00 wartość-trzeba tego pilnować. W przypadku gdy wcześniej ładujemy obiekt z DB a potem go modyfikujemy, ReleaseDate jest ustawiony i nie nadpisany zerową wartością.
Pewnie można by poszukać i podpowiedzieć, które właściwości zostały zmienione, ale to wymagałoby znajomości voodoo.

Dodam tylko dla optymalizacji, że gdy wyciągamy obiekt z EF, który nie będzie zmieniany, np. tylko żeby wyciągnąć i wyświetlić, warto użyć opcji `
AsNoTracking, aby podpowiedzieć EF zaprzestania śledzenia obiektów, bo zniknąć one z naszego kontekstu.

Jeśli zaistnieje potrzeba ponownego podpięcia, można zrobić attach lub update – ale to już wiesz.

 

Szczegóły tutej:
https://learn.microsoft.com/en-us/ef/core/change-tracking/

ps.
Tak naprawdę to nie korzystam z EF.

ps2.
Źródło całego pliku cs dostępne tutaj:
https://gist.github.com/jstadnicki/9a97619c26e4ba9571fa2aec40be25d3

Logowanie dla leniwych

Nie lubię logowania

Nie lubię logowania — brudzi mi w kodzie; narzekam, gdy podczas ratowania produkcji brak logów — jak żyć?!

Jestem leniwy, nie lubię pamiętać o tym i tamtym — lubię, gdy rzeczy dzieją się same i nie muszę sobie zawracać tym głowy. Ale bez przesady! Logi to najgorsze i najlepsze co może spotkać programistów — z jednej strony logi zaśmiecają kod, z drugiej strony dają podgląd na to, co użytkownicy wymyślili na produkcji.

AOP

Logowanie przy pomocy AOP oczyszcza kod w przypadku typowego i klasycznego podejścia do logów, ok — może to zrobić. Często szukam w logach tego, co zostało uruchomione i z jakim parametrem, aby zrozumieć, co poszło nie tak. Nie lubię ^C^V takiego kodu na początku każdej metody. Niestety nie znalazłem dobrego rozwiązania AOP (czytaj darmowego) a interfejs IIntercept z jakiegoś powodu nie chce zostać uruchomiony (ale jest tworzony — kiedyś to zrozumiem); z tego powodu przy pomocy internetów znalazłem DispatchProxy które świetnie robi swoją robotę.

DispatchProxy

Otóż chciałbym mieć możliwość logowania tego, co i jak się wywołuje z możliwością włączenia / wyłączenia detali (poziomy logowania w moim przypadku, lub inna forma konfiguracji). Chce móc przeczytać logi i dowiedzieć się co się wywołuje i z jakim parametrami, dla każdej metody, dodatkowo bez potrzeby modyfikacji kodu, gdy dana metoda się zmieni.

Można to zrobić przy pomocy DispatchProxy które przechwyci wywołanie metody i umożliwi wykonanie dowolnego kodu; przed; po czy też podczas błędu.

Otóż taka klasa opina ładnie swoją docelową klasę T , a następnie podczas tworzenia zwraca takie proxy, udając klasę typu podstawowego. Tutaj uwaga: konstruktor musi być pusty, a ewentualne przekazanie parametrów i ich  przypisanie następuje w metody fabrykującej (wewnątrz if)

Gdy użytkownik takiej klasy wywołuje metodę docelową, na proxy wcześniej zostanie zawołane Invoke gdzie można czynić cuda.
Ja na przykład na poziomie `Information` loguje nazwę wywoływanej metody, a na poziomie Debug dorzucam także nazwy i wartości przekazanych argumentów.

Rejestracja

Piękne jest to, że DispatchProxy nie wymaga zewnętrznych bibliotek i działa na czysto z .net. Zależność do klasy rejestruje w taki sposób:

Akcja

Działa to tak:

Tutaj przykład konsumpcji oraz wykorzystania przykładowego serwisu

A tutaj logi:

Serwis jest nudny do bólu i prawie o nim zapomniałem:

Siup

W tak przygotowanym kodzie, zależności rejestrowane z pomocą logera powinny samodzielnie wrzucać informację o tym, co i jak się wykonało. Oczywiście można dodać sobie flagi do optymalizacji tego, czy przed, czy po co w sytuacjach wyjątkowych ma się logować i na jakim poziomie. Warto także pomyśleć o tym, jak takie logi ze sobą łączyć za pomocy correlationId – ale to pozostawiam już waszej wyobraźni.

 

ps.
dzisiaj bez obrazka