Elasticsearch خدمة لوحدها
Elasticsearch محرك بحث وتحليل موزّع. بتبعتله داتا، يعملها indexing، وبعدين تبحث فيها. معاه full-text search، ترتيب، فلاتر، aggregations، fuzzy، autocomplete، و highlighting. ومتصمم يتوزع على أكتر من آلة لما الداتا أو الترافيك يكبروا.
مش package تحطه جوه الـ API وخلاص. عملية لوحدها ليها RAM وديسك وبورت. في التطوير تشتغل بـ Docker جنب الـ API والداتابيز. في الإنتاج يا تدير الـ cluster بنفسك يا تاخد خدمة جاهزة. نفس أفكار البحث موجودة في OpenSearch، والتفاصيل بتختلف. الأمثلة هنا على Elasticsearch.
من المستند للكلمة، ولا من الكلمة للمستند؟
الطريقة الساذجة: مع كل بحث تلف على العشر مليون منتج وتشوف الكلمة موجودة ولا لأ. ده document → words.
وقت الـ indexing، Elasticsearch يبني هيكل عكسي: inverted index. كل كلمة، وجنبها المستندات اللي ظهرت فيها. البحث بقى word → documents.
| الكلمة | مستندات |
|---|---|
iphone | موبايل iPhone 15 Pro، كفر compatible with iPhone |
pro | موبايل iPhone 15 Pro |
case | الكفر بس |
لما اليوزر يكتب iphone، المحرك مش بيبتدي من المنتج الأول. بيروح لسطر الكلمة في الفهرس ويطلّع الليست.
المستند في Elasticsearch مش صف SQL. مستند البحث بتاع المنتج ممكن يبقى JSON فيه الاسم والوصف والبراند والسعر والتقييم والمخزون، حتى لو في SQL دول خمس جداول. شكل المستند ده هنبنيه في فصل المزامنة.
الفهرس مش ببلاش وقت الكتابة
كل ما تحفظ منتج أو تغيّر سعره، الفهرس لازم يتحدث. القراءة بتبقى سريعة عشان الكتابة دفعت التمن قبل كده. وعشان كده Elasticsearch near-real-time: بعد ما الـ index ينجح، البحث ممكن يفضل ثانية لحد ما يحصل refresh (الافتراضي حوالي ثانية). متستناش إن الطلب اللي بعده على طول يشوف التعديل.
🧪 Lab
شغّل Elasticsearch محليًا. الملف ده للتطوير بس: الأمان مقفول عشان متتعطلش في أول تجربة. في الإنتاج سيبه شغال وابعت API key.
services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:8.17.0 environment: - discovery.type=single-node - xpack.security.enabled=false - ES_JAVA_OPTS=-Xms512m -Xmx512m ports: - "9200:9200"على لينكس لو الـ container وقع بدري، دور على vm.max_map_count. راجع رسالة Docker، متخمّنهاش.
import { Client } from "@elastic/elasticsearch";export const es = new Client({ node: process.env.ELASTIC_URL ?? "http://localhost:9200",});import { es } from "./es.js";const index = "products";const exists = await es.indices.exists({ index });if (!exists) await es.indices.create({ index });await es.index({ index, id: "p-1001", refresh: true, document: { name: "Apple iPhone 15 Pro", description: "موبايل بكاميرا 48 ميجا.", },});const found = await es.search({ index, query: { match: { name: "iphone" } },});console.log(found.hits.hits.map((hit) => hit._source.name));refresh: true هنا عشان التجربة تشوف المستند فورًا. في مسار الكتابة الحقيقي سيبه للـ refresh العادي، عشان متعملش refresh مع كل طلب.