NodeJs Üzerinde ElasticSearch
Selamlar,
https://youtu.be/D4LZMMF9V48
Bu makalede NodeJs üzerinde ElasticSearch ile arama yapacağız. ElasticSearch hakkında daha detaylı bilgeye bu makaleden erişebilirsiniz. Elasticsearch kurulduktan sonra, Mac ortamı için Bash’de “Elasticsearch” komutu yazılarak “http://localhost:9200” portundan ElasticSearch’ün ayağa kalkması sağlanır. Ayrıca monitor amaçlı yine, bash ortamında “kibana” komutu yazılarak “http://localhost:5601” portundan kibana’nın ayağa kalkması sağlanır.
Yeni bir NodeJs projesi oluşturulur. İlgili “service.js” altında, ElasticSearch aşağıdaki komut ile projeye eklenir.
1 |
npm i elasticsearch --save |
elasticClient.js: ElasticSearch client’ı, localhost:9200’e bağlanılır ve dışarıya export edilir.
1 2 3 4 5 6 7 |
const es = require('elasticsearch'); const esClient = new es.Client({ host: 'localhost:9200', log: 'trace' }); module.exports = esClient; |
service.js: ElastichSearch çalışıyor mu diye önce bir ping atalım. Aşağıda görüldüğü gibi ilgili adımlar kodlanır :
- “elasticClient = require(‘./elasticClient’)” : elasticClient kütüphanesi eklenir.
- “elasticClient.ping({” : Elasticsearch’e ping atılarak ayakta olup olmadığına bakılır.
- “if (error) {” : Hata olması durumundan ElasticSearch ayakta değil demektir.
1 2 3 4 5 6 7 8 9 10 11 |
const elasticClient = require('./elasticClient'); elasticClient.ping({ requestTimeout: 1000 }, function (error) { if (error) { console.trace('Elasticsearch\'e erişilmiyor!'); } else { console.log('Elasticsearch ayakta :)'); } }); |
index.js: Sıra geldi Elasticsearch’ün olmazsa olmazlarından Index yaratmaya.
- ” await elasticClient.indices.get({ index: indexName })” : Öncelikle index yok ise yaratılması için, varmı diye ilgili index adına bakılır.
- “return await esClient.indices.create({ index: indexName })” : Belirtilen isme gore index yaratılır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const elasticClient = require('./elasticClient'); const createIndex = async function (indexName) { try { return await elasticClient.indices.get({ index: indexName }); } catch (e) { return await esClient.indices.create({ index: indexName }); } } module.exports = createIndex; |
mapping.js: Sıra geldi Mapping yaratmaya. Yani saklanacak data modeline ait Schema’nın belirlenmesine.
1 2 3 4 5 6 7 8 9 10 11 |
const esClient = require('./elasticClient'); const addmappingToIndex = async function (indexName, mappingType, mapping) { console.log(mapping); return await esClient.indices.putMapping({ index: indexName, type: mappingType, body: mapping }); } module.exports = addmappingToIndex; |
document.js: Her bir kayıda ait satırın, Index’e atıldığı yer burasıdır.
1 2 3 4 5 6 7 8 9 10 11 |
const esClient = require('./elasticClient'); const insertDoc = async function (indexName, _id, mappingType, data) { return await esClient.index({ index: indexName, type: mappingType, id: _id, body: data }); } module.exports = insertDoc; |
service.js: Şimdi yukarıda tanımlı tüm kütüphaneler service.js’e en tepede eklenip, ElasticSearch’ün çalışıp çalışmadığı kontrol edilip, Index yaratılıp, atılıcak data için Mapping oluşturulup en sonunda örnek amaçlı üç Document kaydedilmiştir.
- “const resp = await elasticIndex(‘games’)” : “games” adında bir index yok ise oluşturulur.
- “const mapping = { properties: { title: { type: “text” }, tags: { type: “keyword” }, body: { type: “text” }, age: { type: “integer” } } }” : Belirtilen data tipi yani model, maping olarak ElasticSearch’e atılır.
- “const resmap = await elasticMap(‘games’, ‘categorystore’, mapping)” : “games” indexine “GameMap” atanır.
- “const resdocumnet = await elasticDocument(‘games’, i+1, ‘categorystore’, data[i])” : For döngüsü içinde, her bir game document’ı, ElasticSearch’de “games” Index’ine atanır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
const elasticClient = require('./elasticClient'); const elasticIndex = require('./index'); const elasticMap = require('./mapping'); const elasticDocument = require('./document'); elasticClient.ping({ requestTimeout: 1000 }, async function (error) { if (error) { console.trace('Elasticsearch\'e erişilmiyor!'); } else { console.log('Elasticsearch ayakta :)'); try { //CreateIndex const resp = await elasticIndex('games'); console.log("Index:" + resp); //Create GameMap const mapping = { properties: { title: { type: "text" }, tags: { type: "keyword" }, body: { type: "text" }, age: { type: "integer" } } } const resmap = await elasticMap('games', 'categorystore', mapping); //----------- //Elastic Searc'e Data Atma var data = [ { title: "Call Of Duty", tags: ['War', 'Shooter'], body: "It is about 2.Word War", age: 13 }, { title: "Mortal Kombat 11", tags: ['Fight', 'Violance'], body: "It is about killing each other", age: 20 }, { title: "Death Stranding", tags: ['Delivery', 'Beach'], body: "It is about deleviring package to it's owner.", age: 18 } ]; for (i = 0; i < data.length; i++) { const resdocumnet = await elasticDocument('games', i+1, 'categorystore', data[i]); } } catch (e) { console.log(e); } } }); |
ElasticSearch’ü Kibana haricinde görüntülemek için ben yukarıda görülen, “Chrome ElasticSearch-Head Extension” kullanıyorum. Kesinlikle tavsiye ederim.
Örnek amaçlı games index’ine atılan üç document, aşağıdaki gibi görülmektedir.
Gelin şimdi ilk aramamızı yapalım :
search.js: Aşağıda görüldüğü gibi arama için search dosyası yapılmıştır. İçinde aranacak index, hangi map’de aranacağı ve query tanımlanmıştır.
1 2 3 4 5 6 7 8 9 10 |
const elasticClient = require('./elasticClient'); const search = async function (indexName, mappingType, searchQuery) { return await elasticClient.search({ index: indexName, type: mappingType, body: searchQuery }); } module.exports = search; |
service.js:
- “const elascticSearch = require(‘./search’)” : Arama işlemi için “search” dosyası, sayfaya eklenir.
- “match_phrase_prefix: {” : İçinde geçen bir çeşit like tanımlaması yapılır.
- “title”: “Death”: Başlığında “Death” geçen tüm documentler bulunur.
- “const resSearch = await elascticSearch(‘games’, ‘categorystore’, body)” : Arama işlemi ilgili games index’inin, “categorystore” isimli mapinde belirlenen query ile yapılır.
- “console.log(
Adı: ${resSearch.hits.hits[0]._source.title}\nAçıklama: ${resSearch.hits.hits[0]._source.body}
) : Bulunan ilk sonucun title ve body’si console’a yazılır.
1 2 3 4 5 6 7 8 9 10 11 |
const elasticSearch = require('./search'); const body = { query: { match_phrase_prefix: { "title": "Death" } } } const resSearch = await elascticSearch('games', 'categorystore', body); console.log(`Adı: ${resSearch.hits.hits[0]._source.title}\nAçıklama: ${resSearch.hits.hits[0]._source.body}`); |
Arama sonucu olarak, aşağıdaki gibi bir cevap alınır:
Arama ve Taglara Göre Aggregation :
service.js:
- “query: { match: { “title”: “Duty” } }” : Aşağıda, başlığında “Duty” geçen oyunlar bulunmuştur.
- “aggs: { tags: { terms: { field: ‘tags’ } } }” : Daha sonra bulunan oyunların taglarına göre, içinde geçtikleri döküman sayıları toplamı hesaplanmıştır.
- “resagSearch.aggregations.tags.buckets[0].key”: Bulunan oyunun ilk tagı ekrana basılır. “buckets[1]“‘si de, ilgili oyunun 2.tagıdır.
- “resagSearch.aggregations.tags.buckets[0].doc_count” : Bulunan oyunun ilk tagı’nın geçtiği, toplam döküman sayısı ekrana basılır. “buckets[1]“‘si de ilgili oyunun, 2. tagının geçtiği toplam döküman sayısıdır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
const body = { query: { match: { "title": "Duty" } }, aggs: { tags: { terms: { field: 'tags' } } } } const resagSearch = await elascticSearch('games', 'categorystore', body); console.log(`Adı: ${resagSearch.hits.hits[0]._source.title}\nAçıklama: ${resagSearch.hits.hits[0]._source.body}`); console.log(`Aggregations Key1 - Document Count: ${resagSearch.aggregations.tags.buckets[0].key} - ${resagSearch.aggregations.tags.buckets[0].doc_count}`); console.log(`Aggregations Key2 - Document Count: ${resagSearch.aggregations.tags.buckets[1].key} - ${resagSearch.aggregations.tags.buckets[1].doc_count}`); |
Arama sonucu olarak aşağıdaki gibi bir cevap alınır:
Son olarak, ElasticSearch’e yeni eklenen games Index’inin Kibana’da görüntülenmesi için, aşağıdaki gibi bir yöntemin ile index eklenmesi gerekmektedir.
Bu makalede NodeJs üzerinde ElasticSearch konusuna değindik. NodeJS üzerinde ElasticSearch implementasyonu ne kadar basit ve hızlı olduğunu hepberaber gördük. Once ElasticSearch’ü ayağa kaldırdık. Sonra bir Index oluşturduk. Daha sonra atılacak kayıt kümesi için mapping oluşturduk. Sonra tüm kayıdı, row row document olarak attık. Ve en sonunda da isteğimiz filitrelere göre ElasticSearch’e sorgular attık. ElasticSearch, sadece aramalarda değil, bir çok farklı raporlamalarda da büyük kolaylık sağlamaktadır. Örneğin, içinde konfor tagı geçen tüm yorumların sayısı istenir ise, buna erişim ElasticSearch ile saniyeler sürmektedir.
Geldik bir makalenin daha sonuna. Yeni bir makalede görüşmek üzere hepinize hoşçakalın.
Source:
VS Code üzerinden gerekli javascript dosyalarını oluşturdum. Ctrl + F5 ile çalıştırdığımda Debug Console’da “ElasticSearch Ayakta :)”
mesajını aldım. Chrome extension olan “elasticsearch-head” i kurduktan sonra, bağlantıyı gerçekleştirdim, fakat index atılmadığı için
indexler boş gözüküyordu. İndex atmaya çalıştığımda ElasticSearch’e erişememeye başladım. Kodlardan index oluşturmayı ve veri atmayı
yorum satırına çekip sadece ES bağlanmaya çalıştım.
Aldığım hata;
Elasticsearch WARNING: 2019-12-26T07:01:09Z
Unable to revive connection: http://localhost:9200/
Elasticsearch WARNING: 2019-12-26T07:01:09Z
No living connections
Yardımcı olurmusunuz Bora Bey, teşekkürler.