Pernah nggak sih kamu ngerasa frustrasi waktu fitur pencarian di aplikasi yang kamu buat hasilnya nggak akurat? User ngetik “sepatu running”, tapi yang muncul malah produk sepatu formal, atau parahnya lagi—kosong total. Kalau kamu pakai query SQL biasa dengan LIKE '%keyword%', masalah kayak gini sudah jadi langganan. String matching biasa memang terbatas. Dia nggak bisa pahami konteks, nggak bisa urutkan berdasarkan relevansi, dan performanya juga jelek kalau data sudah jutaan.

Nah, di situlah ElasticSearch masuk sebagai penyelamat. Dia bukan sekadar database pencarian biasa—dia mesin pencari yang benar-benar dibangun untuk kebutuhan full-text search. Google-nya data aplikasi kamu, kalau boleh saya istilahkan.

Saya pribadi sudah pakai ElasticSearch di beberapa project, mulai dari e-commerce yang katalognya ratusan ribu produk, sampai platform knowledge base yang artikelnya ribuan. Dan jujur, once kamu terbiasa pakai ElasticSearch, kamu bakal heran kenapa dulu susah-susah bikin fitur pencarian pakai cara manual.

Di artikel ini, saya mau berbagi pengalaman cara pakai ElasticSearch untuk full-text search di aplikasi kamu. Dari instalasi, bikin index, mengirim data, sampai menulis query yang powerful. Semuanya dibahas step by step. Yuk mulai.

Sebelum masuk ke teknis, penting buat paham dulu apa sebenarnya ElasticSearch itu. Secara sederhana, ElasticSearch adalah mesin pencari dan analytics engine yang bersifat open-source, dibangun di atas Apache Lucene. Dia menyimpan data dalam format JSON dan mengindeksnya secara otomatis supaya proses pencarian jadi super cepat.

Kenapa dia cocok banget untuk full-text search? Ada beberapa alasan utama:

Pertama, inverted index. Ini adalah rahasia di balik kecepatan ElasticSearch. Kalau database relasional menyimpan data baris per baris, ElasticSearch membaliknya—dia menyimpan mapping dari setiap kata ke dokumen mana saja yang mengandung kata itu. Jadi waktu user ngetik “sepatu running”, ElasticSearch nggak perlu scan semua data satu-satu. Dia langsung tahu dokumen mana yang relevan.

Kedua, scoring dan relevansi. ElasticSearch menggunakan algoritma BM25 (evolusi dari TF-IDF) untuk menghitung skor relevansi setiap dokumen. Artinya, hasil pencarian diurutkan berdasarkan seberapa cocok dokumen itu dengan query user—bukan asal muncul.

Ketiga, fitur lanjutan yang kaya. Fuzzy search (toleran typo), synonym, highlighting, autocomplete, bahkan geolocation search—semuanya bisa dilakukan dengan ElasticSearch tanpa perlu library tambahan.

Keempat, skalabilitas horizontal. Kalau data kamu makin besar, kamu tinggal tambah node. ElasticSearch dirancang untuk distributed computing sejak awal.

Bayangkan ElasticSearch seperti perpustakaan super canggih. Kalau perpustakaan biasa menyusun buku berdasarkan rak dan nomor, ElasticSearch membuat indeks terbalik di mana setiap kata kunci punya daftar buku yang relevan. Jadi pustakawan (query engine) bisa langsung kasih kamu buku yang tepat dalam hitungan milidetik.

Instalasi dan Setup ElasticSearch dari Nol

Oke, sekarang kita mulai praktik. Ada beberapa cara buat pasang ElasticSearch, tapi saya sarankan pakai Docker supaya nggak ribet dengan dependency sistem.

Jalankan ElasticSearch dengan Docker

docker run -d \
  --name elasticsearch \
  -p 9200:9200 \
  -p 9300:9300 \
  -e "discovery.type=single-node" \
  -e "xpack.security.enabled=false" \
  -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
  docker.elastic.co/elasticsearch/elasticsearch:8.12.0

Kalau nggak mau pakai Docker, kamu juga bisa install langsung. Untuk Ubuntu:

wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elastic.gpg
echo "deb [signed-by=/usr/share/keyrings/elastic.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
sudo apt update && sudo apt install elasticsearch
sudo systemctl start elasticsearch

Setelah jalan, cek apakah ElasticSearch sudah hidup:

curl -X GET "localhost:9200"

Kalau berhasil, kamu bakal dapat response JSON yang isinya versi ElasticSearch, cluster name, dan info lainnya. Berarti dia sudah siap dipakai.

Saya juga sangat menyarankan pasang Kibana, terutama buat development. Kibana adalah dashboard visual buat ElasticSearch. Kamu bisa coba query langsung, lihat index, monitor performa, dan debugging jadi jauh lebih mudah.

docker run -d \
  --name kibana \
  -p 5601:5601 \
  -e "ELASTICSEARCH_HOSTS=http://elasticsearch:9200" \
  --link elasticsearch \
  docker.elastic.co/kibana/kibana:8.12.0

Buka http://localhost:5601 di browser, dan kamu sudah punya Dev Tools buat nulis query ElasticSearch secara interaktif.

Membuat Index dan Memasukkan Data

Istilah “index” di ElasticSearch itu mirip dengan “table” di database relasional. Bedanya, index di ElasticSearch lebih fleksibel—dia bisa otomatis mendeteksi tipe data field-nya (dynamic mapping) atau kamu bisa definisikan manual (explicit mapping).

Membuat Index dengan Custom Mapping

Untuk full-text search, sangat disarankan bikin mapping manual supaya kamu bisa kontrol analyzer yang dipakai. Ini penting karena analyzer menentukan bagaimana teks dipecah menjadi token (proses yang disebut tokenization).

curl -X PUT "localhost:9200/products" -H 'Content-Type: application/json' -d '{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0,
    "analysis": {
      "analyzer": {
        "indonesian_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "stop", "snowball"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "indonesian_analyzer",
        "boost": 2
      },
      "description": {
        "type": "text",
        "analyzer": "indonesian_analyzer"
      },
      "category": {
        "type": "keyword"
      },
      "price": {
        "type": "float"
      },
      "tags": {
        "type": "keyword"
      },
      "created_at": {
        "type": "date"
      }
    }
  }
}'

Perhatikan di sana: field name dan description pakai tipe text dengan custom analyzer. Ini yang bikin pencarian full-text bisa jalan. Field category dan tags pakai tipe keyword karena nilainya persis (exact match)—cocok buat filtering.

Mengirim Data ke Index

Sekarang kita masukkan beberapa dokumen sample:

curl -X POST "localhost:9200/products/_doc/1" -H 'Content-Type: application/json' -d '{
  "name": "Sepatu Running Nike Air Zoom",
  "description": "Sepatu lari ringan dengan teknologi Zoom Air untuk kenyamanan maksimal saat berlari jarak jauh",
  "category": "sepatu",
  "price": 1850000,
  "tags": ["running", "olahraga", "nike"],
  "created_at": "2026-01-15"
}'

curl -X POST "localhost:9200/products/_doc/2" -H 'Content-Type: application/json' -d '{
  "name": "Sepatu Formal Kulit Pria",
  "description": "Sepatu formal berbahan kulit asli, cocok untuk acara resmi dan ke kantor",
  "category": "sepatu",
  "price": 950000,
  "tags": ["formal", "kulit", "kantor"],
  "created_at": "2026-02-20"
}'

curl -X POST "localhost:9200/products/_doc/3" -H 'Content-Type: application/json' -d '{
  "name": "Tas Ransel Running Waterproof",
  "description": "Tas ransel anti air khusus untuk aktivitas lari dan olahraga outdoor",
  "category": "tas",
  "price": 450000,
  "tags": ["running", "outdoor", "waterproof"],
  "created_at": "2026-03-10"
}'

Kamu juga bisa bulk insert kalau datanya banyak:

curl -X POST "localhost:9200/products/_bulk" -H 'Content-Type: application/json' -d '
{"index":{"_id":"4"}}
{"name":"Kaos Running Dri-Fit","description":"Kaos olahraga bahan cepat kering untuk lari pagi","category":"pakaian","price":185000,"tags":["running","kaos","olahraga"],"created_at":"2026-04-01"}
{"index":{"_id":"5"}}
{"name":"Sepatu Trail Hiking Salomon","description":"Sepatu hiking dan trail running dengan grip kuat untuk medan berat","category":"sepatu","price":2200000,"tags":["hiking","trail","salomon"],"created_at":"2026-04-15"}
'

Kalau kamu pakai bahasa pemrograman seperti Python, library elasticsearch-py bikin semua proses ini jadi lebih rapi:

from elasticsearch import Elasticsearch

es = Elasticsearch("http://localhost:9200")

# Index satu dokumen
es.index(
    index="products",
    id=6,
    document={
        "name": "Jam Tangan GPS Running Garmin",
        "description": "Jam tangan pintar dengan GPS dan fitur tracking untuk pelari profesional",
        "category": "aksesoris",
        "price": 4500000,
        "tags": ["running", "garmin", "gps"],
        "created_at": "2026-05-01"
    }
)

# Bulk insert
from elasticsearch.helpers import bulk

docs = [
    {"_index": "products", "_id": 7, "_source": {"name": "Kaos Kaki Running Compression", "description": "Kaos kaki compression untuk meningkatkan sirkulasi darah saat lari", "category": "aksesoris", "price": 95000, "tags": ["running", "compression"], "created_at": "2026-05-10"}},
    {"_index": "products", "_id": 8, "_source": {"name": "Sepatu Casual Sneakers Adidas", "description": "Sepatu sneakers nyaman untuk gaya sehari-hari", "category": "sepatu", "price": 1100000, "tags": ["casual", "adidas", "sneakers"], "created_at": "2026-06-01"}},
]

bulk(es, docs)

Ini bagian yang paling seru. Setelah data masuk, saatnya kita bikin query pencarian. ElasticSearch punya banyak jenis query, tapi saya akan fokus ke yang paling sering dipakai untuk full-text search.

curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '{
  "query": {
    "match": {
      "description": "sepatu lari"
    }
  }
}'

Query di atas akan mencari semua dokumen di mana field description mengandung kata “sepatu” ATAU “lari” (default operator-nya OR). Hasilnya diurutkan berdasarkan skor relevansi. Dokumen yang mengandung kedua kata sekaligus bakal dapat skor lebih tinggi.

Kalau mau semua kata harus ada, tambahkan operator AND:

curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '{
  "query": {
    "match": {
      "description": {
        "query": "sepatu lari",
        "operator": "and"
      }
    }
  }
}'

Multi-Match Query — Cari di Beberapa Field Sekaligus

Ini yang paling sering saya pakai di production. User biasanya nggak peduli apakah kata kuncinya ada di nama produk atau deskripsi—mereka cuma mau hasil yang relevan.

curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '{
  "query": {
    "multi_match": {
      "query": "running sepatu",
      "fields": ["name^3", "description", "tags^2"],
      "type": "best_fields",
      "fuzziness": "AUTO"
    }
  }
}'

Perhatikan name^3 dan tags^2—itu artinya field name diberi bobot 3 kali lebih tinggi dari field default, dan tags diberi bobot 2 kali. Jadi kalau kata kunci ketemu di nama produk, skornya bakal lebih tinggi dibanding ketemu di deskripsi.

Parameter fuzziness: "AUTO" bikin pencarian toleran terhadap typo. Kalau user ngetik “sepatu runing” (kurang satu huruf), ElasticSearch tetap bisa menemukan hasil yang benar.

Bool Query — Kombinasikan Full-Text dengan Filter

Di aplikasi nyata, pencarian biasanya nggak cuma full-text aja. User mungkin mau filter berdasarkan kategori, range harga, atau tanggal. Di sinilah bool query sangat berguna:

curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "running",
            "fields": ["name^3", "description", "tags^2"]
          }
        }
      ],
      "filter": [
        {
          "term": {
            "category": "sepatu"
          }
        },
        {
          "range": {
            "price": {
              "gte": 500000,
              "lte": 3000000
            }
          }
        }
      ]
    }
  },
  "sort": [
    { "_score": "desc" },
    { "price": "asc" }
  ],
  "highlight": {
    "fields": {
      "name": {},
      "description": {}
    }
  }
}'

Ada tiga bagian penting di bool query:

  • must: klausa yang WAJIB terpenuhi dan mempengaruhi skor relevansi
  • filter: klausa yang wajib terpenuhi TAPI nggak mempengaruhi skor (dan hasilnya di-cache, jadi lebih cepat)
  • should: klausa yang opsional, kalau terpenuhi skor naik

Yang saya suka, highlight di bagian bawah bikin kamu bisa nampilkan potongan teks yang mengandung kata kunci (biasanya di-wrap pakai tag <em>)—sangat berguna buat tampilan search result di frontend.

Contoh Implementasi di Python

Sekarang mari kita lihat bagaimana ini diimplementasikan di kode Python yang bisa kamu pakai di aplikasi:

from elasticsearch import Elasticsearch

es = Elasticsearch("http://localhost:9200")

def search_products(keyword, category=None, min_price=None, max_price=None, page=1, size=10):
    """Full-text search produk dengan filter opsional."""

    must_clause = [
        {
            "multi_match": {
                "query": keyword,
                "fields": ["name^3", "description", "tags^2"],
                "type": "best_fields",
                "fuzziness": "AUTO"
            }
        }
    ]

    filter_clause = []

    if category:
        filter_clause.append({"term": {"category": category}})

    if min_price or max_price:
        price_range = {}
        if min_price:
            price_range["gte"] = min_price
        if max_price:
            price_range["lte"] = max_price
        filter_clause.append({"range": {"price": price_range}})

    body = {
        "query": {
            "bool": {
                "must": must_clause,
                "filter": filter_clause
            }
        },
        "from": (page - 1) * size,
        "size": size,
        "highlight": {
            "fields": {
                "name": {"pre_tags": ["<mark>"], "post_tags": ["</mark>"]},
                "description": {"pre_tags": ["<mark>"], "post_tags": ["</mark>"]}
            }
        }
    }

    response = es.search(index="products", body=body)

    results = []
    for hit in response["hits"]["hits"]:
        results.append({
            "id": hit["_id"],
            "score": hit["_score"],
            "source": hit["_source"],
            "highlights": hit.get("highlight", {})
        })

    return {
        "total": response["hits"]["total"]["value"],
        "page": page,
        "size": size,
        "results": results
    }


# Contoh pemakaian
hasil = search_products(
    keyword="sepatu running",
    category="sepatu",
    min_price=100000,
    max_price=3000000
)

for item in hasil["results"]:
    print(f"[{item['score']:.2f}] {item['source']['name']} — Rp{item['source']['price']:,}")
    if 'description' in item['highlights']:
        print(f"  Highlight: {item['highlights']['description'][0]}")
    print()

Contoh di Node.js/Express

Buat yang pakai JavaScript, ini implementasi sederhananya:

const { Client } = require('@elastic/elasticsearch');
const client = new Client({ node: 'http://localhost:9200' });

async function searchProducts(keyword, { category, minPrice, maxPrice, page = 1, size = 10 } = {}) {
  const filterClause = [];

  if (category) {
    filterClause.push({ term: { category } });
  }

  if (minPrice || maxPrice) {
    const priceRange = {};
    if (minPrice) priceRange.gte = minPrice;
    if (maxPrice) priceRange.lte = maxPrice;
    filterClause.push({ range: { price: priceRange } });
  }

  const { body } = await client.search({
    index: 'products',
    body: {
      query: {
        bool: {
          must: [{
            multi_match: {
              query: keyword,
              fields: ['name^3', 'description', 'tags^2'],
              type: 'best_fields',
              fuzziness: 'AUTO'
            }
          }],
          filter: filterClause
        }
      },
      from: (page - 1) * size,
      size,
      highlight: {
        fields: {
          name: {},
          description: {}
        }
      }
    }
  });

  return {
    total: body.hits.total.value,
    results: body.hits.hits.map(hit => ({
      id: hit._id,
      score: hit._score,
      ...hit._source,
      highlights: hit.highlight || {}
    }))
  };
}

// Contoh pemakaian
app.get('/api/search', async (req, res) => {
  const { q, category, min_price, max_price, page } = req.query;
  const results = await searchProducts(q, {
    category,
    minPrice: min_price ? Number(min_price) : undefined,
    maxPrice: max_price ? Number(max_price) : undefined,
    page: page ? Number(page) : 1
  });
  res.json(results);
});

Tips dan Best Practice dari Pengalaman Saya

Setelah pakai ElasticSearch di berbagai project, saya pelajari beberapa hal yang bikin implementasi jadi lebih baik:

1. Jangan Lupa Sinkronisasi Data

ElasticSearch bukan primary database—dia adalah secondary data store yang dioptimasi untuk pencarian. Jadi kamu tetap butuh database utama (MySQL, PostgreSQL, dll). Yang perlu dijaga adalah sinkronisasi antara database utama dan ElasticSearch.

Ada dua pendekatan umum:

  • Sync on write: Setiap kali data berubah di database utama, langsung update juga di ElasticSearch. Cocok untuk data yang sering berubah dan butuh real-time.
  • Async with queue: Pakai message queue (RabbitMQ, Kafka, dll) untuk mengirim event perubahan data ke worker yang akan update ElasticSearch. Lebih scalable dan nggak memperlambat proses utama.
# Contoh sinkronisasi sederhana setelah create/update di database
def update_product_in_db(product_id, data):
    # Update di database utama
    db.products.update(product_id, data)

    # Sinkron ke ElasticSearch
    es.index(
        index="products",
        id=product_id,
        document=data
    )

def delete_product_from_db(product_id):
    db.products.delete(product_id)
    es.delete(index="products", id=product_id)

2. Pakai Alias untuk Index

Ini pelajaran yang saya dapat setelah harus reindex data di production dan downtime selama 10 menit (yang terasa seperti 10 jam). Pakai alias supaya kamu bisa bikin index baru, reindex data, lalu pindahkan alias tanpa downtime:

# Bikin index baru dengan mapping yang diperbarui
curl -X PUT "localhost:9200/products_v2" -H 'Content-Type: application/json' -d '{ ... }'

# Reindex dari index lama ke baru
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d '{
  "source": { "index": "products_v1" },
  "dest": { "index": "products_v2" }
}'

# Pindahkan alias
curl -X POST "localhost:9200/_aliases" -H 'Content-Type: application/json' -d '{
  "actions": [
    { "remove": { "index": "products_v1", "alias": "products" } },
    { "add": { "index": "products_v2", "alias": "products" } }
  ]
}'

3. Monitor Performa Query

ElasticSearch punya API _explain yang bisa kasih tahu kenapa sebuah dokumen dapat skor tertentu. Ini sangat berguna buat debugging:

curl -X GET "localhost:9200/products/_explain/1" -H 'Content-Type: application/json' -d '{
  "query": {
    "match": {
      "name": "sepatu running"
    }
  }
}'

4. Jangan Asal Tambah Field Text

Setiap field bertipe text itu butuh resource buat diindex. Kalau ada field yang nggak pernah dipakai buat pencarian, set sebagai keyword atau integer aja. Ini ngaruh banget ke penggunaan memori dan kecepatan indexing.


Kalau kamu merasa stuck atau butuh bantuan buat setup ElasticSearch di project kamu—entah itu soal arsitektur, optimasi query, atau integrasi dengan stack yang sudah ada—jangan ragu buat hubungi saya di [email protected]. Saya senang bisa diskusi dan bantu cari solusi yang pas buat kebutuhan kamu.


FAQ

Apakah ElasticSearch bisa menggantikan database utama seperti MySQL atau PostgreSQL?

Tidak disarankan. ElasticSearch memang powerful untuk pencarian dan analytics, tapi dia bukan