Python

Python ve Elasticsearch Kullanımı

Selamlar. Bu yazıda sizlere Python ve Elasticsearch Kullanımı hakkında bilgi vereceğim. Önceki yazıda Axios hakkında bilgi sunmuştum.

Elasticsearch Nedir?

ElasticSearch Apache Lucene altyapısından geliştirilmiş, hafif, kolay kurulan, solraçık kaynak kodlu, ücretsiz, ölçeklendirilebilen bir arama motorudur. (Kaynak)

Python ve Elasticsearch Kullanımı

 

Python ve Elasticsearch Kullanımı

Yazıya başlamadan önce belirteyim. Bu yazıda Flask kullanacağım. Sorgularımızı Postman üzerinden göndereceğiz. Ama siz zaten gerçek dünya uygulamasında bunu çok farklı senaryolara göre şekillendireceksiniz.

Gereksinimler

Öncelikle JDK kurulu olmalı. Bu yazıda JDK / JRE nasıl kurulur gibi bir bilgi paylaşmayacağım. Tonla doküman bulunur ve dahası bu kullandığınız Linux dağıtımına, Windows'a ya da Mac OS X'e bağlı şekillerde farklılıklara sahiptir.

Eğer sisteminizde JDK yok ise kurmalısınız.

Kurulum

Yazıda Elasticsearch 6.2.1 sürümüyle anlatım yapacağım. Resmi sitede verilen download linklerinde Debian tabanlı sürümler, Fedora / RedHat ve benzeri sürümler için RPM ve Windows için MSI bulunmakta. Diğerleri içinse ZIP / TAR arşivleri yer almakta.

İndirme Linki: https://www.elastic.co/downloads/past-releases/elasticsearch-6-2-1

Eğer Ubuntu kullanıyorsanız kurulum şöyle olacak:

wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.2.1.deb

sudo dpkg -i elasticsearch-6.2.1.deb

Java bağımlılıkları yüklü olduğu için herhangi bir hata almayacağız. Bu işlemin ardından elastic servisini başlatalım:

sudo systemctl enable elasticsearch

Başlangıç aşaması olduğu için büyük ayarlamalar yapmayacağız ama temel bazı ayarlamaları yapmaya başlamamız öğrenmemiz açısından iyi bir adım olacaktır.

Ayar dosyası elasticsearch.yml dosyasıdır. Bu dosyayı düzenleyeceğiz.

sudo nano /etc/elasticsearch/elasticsearch.yml

Nano ile dosyayı açtığımız için CTRL + W ile cluster.name değerini güncelleyelim istersek clusterımıza elton john adını verebiliriz. Aynı şekilde node değerini de bu dosyadan aratıp bulalım.

cluster.name: eltonjohn
node.name: "Ilk nodeumuz"

Bu işlemden sonra işimiz bitiyor ardından CTRL + X kombinasyonuyla kaydetmek istediğimizi belirtip gelen ekranda Y tuşuna basalım. Elasticsearch servisinin yeniden başlatılması gerekiyor.

sudo systemctl restart elasticsearch

Test için şu curl isteğini konsol ekranından verelim:

curl -X GET 'http://localhost:9200'

Anlaşıldığı üzere elasticsearch şu anda 9200 portunda çalışıyor ancak elasticsearch.yml dosyasında 9200 portunu aratıp özelleştirebilirsiniz. Yukarıdaki istek bana şöyle bir sonuç döndürdü.

{
    "name": "Ilk nodeumuz",
    "cluster_name": "eltonjohn",
    "cluster_uuid": "FArhKpkhSZqjDfs3oSBMHw",
    "version": {
        "number": "6.2.1",
        "build_hash": "7299dc3",
        "build_date": "2018-02-07T19:34:26.990113Z",
        "build_snapshot": false,
        "lucene_version": "7.2.1",
        "minimum_wire_compatibility_version": "5.6.0",
        "minimum_index_compatibility_version": "5.0.0"
    },
    "tagline": "You Know, for Search"
}

Tabi elasticsearch için request yapılacak endpoint herkesin erişebileceği network activity ekranından takip edilebilir olmamalı. Bu nedenden dolayı da bu işlemi Python ve Flask tarafında şöyle gerçekleştirelim:

Virtualenv ile Flask ve Elasticsearch Kurulumu

Öncelikle virtualenv ile Flask kurulumunu yapalım:

virtualenv venv

. venv/bin/activate

pip install Flask

ardından da elasticsearch-py modülünü kuracağız:

pip install elasticsearch

Detaylı bilgi: https://elasticsearch-py.readthedocs.io/en/master/

Python tarafında da bağımlılıklar yüklendi. Şimdi main.py adında bir dosya oluşturalım ve elasticsearch-py modülünü ve Flask'i import edelim.

from datetime import datetime
from flask import Flask, jsonify, request
from elasticsearch import Elasticsearch

Bu işlemden sonra Flask ve Elasticsearch modüllerini initialize edelim.

es = Elasticsearch()

app = Flask(__name__)

Üç adet metod yazacağız. Bu işlemler normalde böyle olmayacak ama ben sadece Elasticsearch olayını anlatıyorum.

@app.route('/', methods=['GET'])
def index():
    results = es.get(index='contents', doc_type='title', id='my-new-slug')
    return jsonify(results['_source'])

Bu ilk index metodu elasticsearch tarafında belirttiğimiz index ve doküman tipine göre id'den sorgulama yapmamıza yardım edecek. Şu anda içerik olmadığı için boş dönecektir.

Burada bilmemiz gereken index, doc_type ve id değerleri tamamen programlanabilir durumdadır. Eğer get metodundan dönen değeri gösterecekseniz _source keyine bakmanız gerekecektir.

@app.route('/insert_data', methods=['POST'])
def insert_data():
    slug = request.form['slug']
    title = request.form['title']
    content = request.form['content']

    body = {
        'slug': slug,
        'title': title,
        'content': content,
        'timestamp': datetime.now()
    }

    result = es.index(index='contents', doc_type='title', id=slug, body=body)

    return jsonify(result)

Bu adımda slug, title ve content adında üç adet form değerini post ettiğimizi varsayalım. Bu dönen değerleri index() metodunun body parametresine vererek yeni ekleme işlemini gerçekleştiriyoruz.

Fark ettiyseniz aynı index, aynı doküman tipi fakat farklı id ve body yani bir bakıma content diyebiliriz. Bu kısmın değişebilir olması aynı zamanda yeni indexler oluşturulabileceğinin de bir kanıtı aynı zamanda.

Dilerseniz siz de bi index oluşturun, doküman tipini değiştirin ve farklı bir slug ekleyin.

Bu arada yolladığınız değerler daha önce indexlenmişse yeni bir insert yerine version update ediliyor.

Gelelim son aşamada arama nasıl yapılıra. Bunun için üçüncü ve son metodumuzu oluşturalım:

@app.route('/search', methods=['POST'])
def search():
    keyword = request.form['keyword']

    body = {
        "query": {
            "multi_match": {
                "query": keyword,
                "fields": ["content", "title"]
            }
        }
    }

    res = es.search(index="contents", doc_type="title", body=body)

    return jsonify(res['hits']['hits'])

Bu örnekte multi_match adında bir özellik kullanacağız. Yani birden fazla body keyine göre sorgulama yapabiliyor olacağız. Bir arama metnini keyword adındaki formdan gelen parametreden alacağız.

Dönen değerdeki hitslerin ikincisi json array olacak yani benzer içeriğe sahip tüm değerler listelenebilir olacaktır. Benim örneğimde şöyle bir sonuç dönüyor:

{
    "_shards": {
        "failed": 0,
        "skipped": 0,
        "successful": 5,
        "total": 5
    },
    "hits": {
        "hits": [
            {
                "_id": "other-my-diff",
                "_index": "contents",
                "_score": 0.2876821,
                "_source": {
                    "content": "What kind of content is this?",
                    "slug": "other-my-diff",
                    "timestamp": "2018-02-11T16:08:10.409353",
                    "title": "Very different title second"
                },
                "_type": "title"
            },
            {
                "_id": "other-my",
                "_index": "contents",
                "_score": 0.2876821,
                "_source": {
                    "content": "What kind of content?",
                    "slug": "other-my",
                    "timestamp": "2018-02-11T16:00:51.613402",
                    "title": "Very different title"
                },
                "_type": "title"
            }
        ],
        "max_score": 0.2876821,
        "total": 2
    },
    "timed_out": false,
    "took": 3
}

Evet tüm aşamalar bu kadar. Burada elasticsearch bir veri tabanı gibi düşünülmemeli. Ben burada bütün içeriği ekledim örnek için. Web sitesinde arama yapılabilir bölümler daha çok index için düşünülebilir.

Bu da başlık ya da content olabilir. En nihayetinde öyle karmaşık join sorgularını vs. burada çalıştırmayacaksınız.

Arama işlemlerinin veri tabanı tarafında halledilmesi gibi sorgu gerektiren noktalarda yardımcı olabilirliği önemli. Tabi bundan çok daha fazlası vardır. Bunu da siz öğrenip göreceksiniz.

Tüm Kodlar

Python ve Elasticsearch Kullanımı hakkında bahsetmiş olduğum bütün kodlar burada bulunmakta. İlgili bağımlılıklar kurulu ise aşağıdaki kod düzgün bir şekilde çalışacaktır.

from datetime import datetime
from flask import Flask, jsonify, request
from elasticsearch import Elasticsearch

es = Elasticsearch()

app = Flask(__name__)

@app.route('/', methods=['GET'])
def index():
    results = es.get(index='contents', doc_type='title', id='my-new-slug')
    return jsonify(results['_source'])


@app.route('/insert_data', methods=['POST'])
def insert_data():
    slug = request.form['slug']
    title = request.form['title']
    content = request.form['content']

    body = {
        'slug': slug,
        'title': title,
        'content': content,
        'timestamp': datetime.now()
    }

    result = es.index(index='contents', doc_type='title', id=slug, body=body)

    return jsonify(result)

@app.route('/search', methods=['POST'])
def search():
    keyword = request.form['keyword']

    body = {
        "query": {
            "multi_match": {
                "query": keyword,
                "fields": ["content", "title"]
            }
        }
    }

    res = es.search(index="contents", doc_type="title", body=body)

    return jsonify(res['hits']['hits'])

app.run(port=5000, debug=True)

Yazının tamamı bu kadar. Bu yazıda Python ve Elasticsearch Kullanımı hakkında temel olarak bilgi sahibi olduk. Ben de bu yazı ile ilk defa Elasticsearch kullanmış oldum. Yani yeni öğreniyorsanız bilgim sizinle aynı durumda. Yazıya katılması gereken ekstra bilgiler varsa ya da hatalı noktalar var ise yorumla belirtmeniz beni ziyadesiyle memnun edecektir.

Okuduğunuz için teşekkür ederim