جستجو با اپن سرچ
اپن سرچ یک موتور جستجوست: سندها را به شکل JSON به آن میدهید و آن سندهایی را که با یک پرسمان جور درمیآیند پیدا میکند، رتبه میدهد و بر اساس هر فیلدی که بخواهید میشمارد. این درس با بستهی دیتابیس.اپن سرچ گام به گام یک جستجوگر کتاب کوچک میسازد.
سروری برای گفتگو
هر نمونهای که با خوشه گفتگو میکند انتظار دارد سروری روی http://localhost:9200 باشد. سادهترین راه داکر است: docker run -p 9200:9200 -e discovery.type=single-node -e DISABLE_SECURITY_PLUGIN=true opensearchproject/opensearch:2.17.1. پشت این صفحه سروری نیست، پس این نمونهها را مترجم بررسی میکند و خروجیای را نشان میدهند که هنگام اجرای پشت سر هم روی یک اپن سرچ ۲.۱۷.۱ واقعی چاپ کردند. نمونههایی که فقط درخواست میسازند یا پاسخ میخوانند در هر ساخت سایت اجرا میشوند.
این بسته بخشی از کتابخانهی استاندارد است و به نصب چیز دیگری نیاز ندارد. آن را با واردسازی دیتابیس.اپن سرچ بیاورید و با نام اپن سرچ صدا بزنید.
اتصال
اتصال نشانی یک گره را میگیرد و یک کارخواه برمیگرداند. تا نخستین فراخوانی چیزی فرستاده نمیشود، پس با پینگ بررسی کنید که خوشه پاسخ میدهد و بعد با دریافت اطلاعات و سلامت خوشه دربارهی خودش بپرسید.
واردسازی دیتابیس.اپن سرچ
تابع ریشه:
ک := اپن سرچ.اتصال("http://localhost:9200")
اگر وارونه اپن سرچ.پینگ(ک):
سرچاپ "پاسخی نیامد:"، اپن سرچ.خطا(ک)
برگشت 1
پایان
آگاهی := اپن سرچ.دریافت اطلاعات(ک)
سرچاپ آگاهی.distribution، آگاهی.version، آگاهی.cluster_name
ه := اپن سرچ.سلامت خوشه(ک)
سرچاپ "سلامت:"، ه.status، ه.nodes، "گره"
برگشت 0
پایانیک راهاندازی واقعی معمولا بیشتر لازم دارد: چند گره، نام کاربری و گذرواژه، مرجع صدور گواهی خودتان و سقف زمان. آنها را در یک پیکربندی بگذارید و به کارخواه تازه بدهید. کارخواه درخواستها را میان گرهها پخش میکند، گرهای را که اتصال را رد کند یک دقیقه کنار میگذارد و درخواستی را که پاسخ 502، 503 یا 504 گرفته روی گرهی دیگری دوباره میفرستد. اتصال بدون وارسی گواهی برای راهاندازی نمایشی با گواهی خودامضاست و اتصال آمازون هر درخواست را با AWS SigV4 برای Amazon OpenSearch Service امضا میکند؛ برای یک مجموعهی Serverless به جای سرویس دامنه از سرویس بیسرور استفاده کنید.
واردسازی دیتابیس.اپن سرچ
تابع ریشه:
ناپایا تنظیم := اپن سرچ.پیکربندی پیشفرض()
تنظیم.nodes.بیفزا("https://node2.example.com:9200, https://node3.example.com:9200")
تنظیم.username = "app"
تنظیم.password = "s3cret-pass"
تنظیم.ca_file = "/etc/ssl/certs/opensearch-ca.pem"
تنظیم.timeout_ms = 10000
تنظیم.max_retries = 2
ک := اپن سرچ.کارخواه تازه(تنظیم)
سرچاپ اپن سرچ.معتبر(ک)، اپن سرچ.گرهها(ک)
محلی := اپن سرچ.اتصال بدون وارسی گواهی("https://localhost:9200"، "admin"، "admin-pass")
سرچاپ اپن سرچ.معتبر(محلی)
آمازون := اپن سرچ.اتصال آمازون(
"https://search-books.eu-west-1.es.amazonaws.com"، "eu-west-1"، اپن سرچ.سرویس دامنه،
"AKIA..."، "secret-key"، ""
)
سرچاپ اپن سرچ.معتبر(آمازون)
پایاننمایه و سندهایش
نمایه جایی است که سندها در آن زندگی میکنند و نگاشت آن میگوید هر فیلد چیست: text برای جستجوی تماممتن به واژهها شکسته میشود، keyword دقیق تطبیق میخورد و عدد و تاریخ را میشود مقایسه و مرتب کرد. اگر نگاشتی ندهید اپن سرچ آن را حدس میزند، ولی حدس را بعدا نمیشود عوض کرد؛ پس بهتر است نمایه را خودتان بسازید. ویژگی یک فیلد مینویسد و ویژگیها آنها را کنار هم میگذارد.
سندها متن JSON هستند و هر نوشتن یک نتیجه نوشتن با شناسه، نتیجه و نسخه برمیگرداند. سند تازه پس از تازهسازی بعدی، حدود یک ثانیه بعد، قابل جستجو میشود؛ تازهسازی با انتظار فراخوانی را تا آن لحظه نگه میدارد که در آزمونها و اسکریپتها کارآمد است. دریافت سندی را با شناسه برمیگرداند و مسیر جیسون مقداری را از منبع آن بیرون میکشد.
واردسازی دیتابیس.اپن سرچ
تابع ریشه:
ک := اپن سرچ.اتصال("http://localhost:9200")
ناپایا ویژگیها := وکتور {} برگردان وکتور<رشته>
ویژگیها.بیفزا(اپن سرچ.ویژگی("title"، "text"))
ویژگیها.بیفزا(اپن سرچ.ویژگی("author"، "keyword"))
ویژگیها.بیفزا(اپن سرچ.ویژگی("year"، "integer"))
ویژگیها.بیفزا(اپن سرچ.ویژگی("stock"، "integer"))
سرچاپ "ساخته شد:"، اپن سرچ.ایجاد نمایه با تنظیمات(ک، "books"، 1، 0، اپن سرچ.ویژگیها(ویژگیها))
ن := اپن سرچ.نمایهسازی با گزینه(ک، "books"، "1"، `{"title":"Shahnameh","author":"Ferdowsi","year":1010,"stock":3}`، اپن سرچ.گزینهها { refresh = اپن سرچ.تازهسازی با انتظار })
سرچاپ "نمایه شد:"، ن.ok، ن.result، ن.version
د := اپن سرچ.دریافت(ک، "books"، "1")
اگر د.found:
سرچاپ "عنوان:"، اپن سرچ.مسیر جیسون(د.source، "title")
پایان
بهروز := اپن سرچ.بهروزرسانی(ک، "books"، "1"، `{"stock":2}`)
سرچاپ "بهروز شد:"، بهروز.result، بهروز.version
حذف شده := اپن سرچ.حذف سند(ک، "books"، "1")
سرچاپ "حذف شد:"، حذف شده.result
پایانچند سند با هم
فرستادن سندها با یک درخواست برای هر کدام کند است. یک دستهای کنشهای نمایهسازی، ایجاد، بهروزرسانی و حذف را جمع میکند و با هم میفرستد. این نمونه فقط دسته را میسازد و بدنهای را که فرستاده خواهد شد چاپ میکند، پس اینجا اجرا میشود:
واردسازی دیتابیس.اپن سرچ
تابع ریشه:
ناپایا ب := اپن سرچ.دستهای تازه()
اپن سرچ.نمایهسازی دستهای(ب، "books"، "1"، `{"title":"Shahnameh","author":"Ferdowsi","year":1010}`)
اپن سرچ.نمایهسازی دستهای(ب، "books"، "2"، `{"title":"Masnavi","author":"Rumi","year":1258}`)
اپن سرچ.نمایهسازی دستهای(ب، "books"، "3"، `{"title":"Golestan","author":"Saadi","year":1258}`)
اپن سرچ.بهروزرسانی دستهای(ب، "books"، "2"، `{"stock":4}`)
اپن سرچ.حذف دستهای(ب، "books"، "99")
سرچاپ اپن سرچ.اندازه دستهای(ب)، "کنش"
چاپ اپن سرچ.بدنه دستهای(ب)
اپن سرچ.آزادسازی دستهای(ب)
پایانارسال دستهای آن را میفرستد. درخواست دستهای حتی وقتی برخی کنشها شکست بخورند در کل موفق است، پس به هر قلم نگاه کنید: ok، status و نوع و علت خطا میگویند برای همان یک سند چه شد.
واردسازی دیتابیس.اپن سرچ
تابع ریشه:
ک := اپن سرچ.اتصال("http://localhost:9200")
ناپایا ب := اپن سرچ.دستهای تازه()
اپن سرچ.نمایهسازی دستهای(ب، "books"، "1"، `{"title":"Shahnameh, the Book of Kings","author":"Ferdowsi","year":1010}`)
اپن سرچ.نمایهسازی دستهای(ب، "books"، "2"، `{"title":"Masnavi","author":"Rumi","year":1258}`)
اپن سرچ.نمایهسازی دستهای(ب، "books"، "3"، `{"title":"Golestan","author":"Saadi","year":1258}`)
اپن سرچ.نمایهسازی دستهای(ب، "books"، "4"، `{"title":"Kings and Heroes","author":"Anonymous","year":1900}`)
پاسخ := اپن سرچ.ارسال دستهای با تازهسازی(ک، ب، اپن سرچ.تازهسازی فوری)
اپن سرچ.آزادسازی دستهای(ب)
سرچاپ پاسخ.succeeded، "ذخیره شد،"، پاسخ.failed، "شکست خورد"
هر قلم در پاسخ.items:
اگر وارونه قلم.ok:
سرچاپ قلم.id، قلم.error_type، قلم.error_reason
پایان
پایان
پایانساختن پرسمان
پرسمان هم JSON است و بسته برای هر گونهی رایج یک سازنده دارد. تطبیق واژههای یک فیلد متنی را جستجو میکند، واژه یک مقدار دقیق میخواهد و بازه، بازه صحیح و بازه تاریخ مقایسه میکنند. برای ترکیبشان یک پرسمان منطقی را پر کنید: must باید تطبیق بخورد و به امتیاز میافزاید، filter باید تطبیق بخورد ولی امتیاز نمیدهد، should امتیاز را بالا میبرد و must_not کنار میگذارد. یک درخواست جستجو صفحهبندی، مرتبسازی و فیلدهای برگشتی را میافزاید؛ بدنه جستجو نشان میدهد چه فرستاده خواهد شد.
واردسازی دیتابیس.اپن سرچ
تابع ریشه:
سرچاپ اپن سرچ.تطبیق("title"، "book of kings")
سرچاپ اپن سرچ.واژه("author"، "Ferdowsi")
سرچاپ اپن سرچ.بازه صحیح("year"، 1000، 1300)
ناپایا شرط := اپن سرچ.پرسمان منطقی {}
شرط.must.بیفزا(اپن سرچ.تطبیق("title"، "kings"))
شرط.filter.بیفزا(اپن سرچ.بازه صحیح("year"، 1000، 1300))
شرط.must_not.بیفزا(اپن سرچ.واژه("author"، "Anonymous"))
سرچاپ اپن سرچ.پرسمان منطقی ساخته(شرط)
ناپایا درخواست := اپن سرچ.درخواست جستجو { query = اپن سرچ.پرسمان منطقی ساخته(شرط)، size = 5 }
درخواست.sort.بیفزا(اپن سرچ.مرتب نزولی("year"))
درخواست.source.بیفزا("title")
سرچاپ اپن سرچ.بدنه جستجو(درخواست)
پایانهر چه سازندهها پوشش نمیدهند را میشود با دست به شکل رشتهی بکتیک نوشت، چون پرسمان فقط متن است. سازندهها، بدنهی درخواست و پاسخ را میشود با JSON خودتان درآمیخت.
خواندن یافتهها
جستجو با درخواست درخواست را اجرا میکند و یک نتیجه جستجو برمیگرداند: شمار کل تطبیقها، زمانی که گرفت و یافتههای این صفحه، بهترین در آغاز. هر یافته شناسه، امتیاز و منبع خود را دارد، به همراه تکههای برجستهشدهای که خواستهاید.
واردسازی دیتابیس.اپن سرچ
تابع ریشه:
ک := اپن سرچ.اتصال("http://localhost:9200")
ناپایا فیلدها := وکتور {} برگردان وکتور<رشته>
فیلدها.بیفزا("title")
ناپایا درخواست := اپن سرچ.درخواست جستجو { query = اپن سرچ.تطبیق("title"، "kings")، size = 10 }
درخواست.highlight = اپن سرچ.برجستهسازی(فیلدها)
ر := اپن سرچ.جستجو با درخواست(ک، "books"، درخواست)
اگر وارونه ر.ok:
سرچاپ ر.error
برگشت 1
پایان
سرچاپ ر.total، "یافته"
هر ه در ر.hits:
سرچاپ ه.id، ه.score، ه.source
پایان
برگشت 0
پایانهمین خواننده روی پاسخی که از پیش دارید هم کار میکند، با تجزیه جستجو. این نمونه به همین شکل اینجا بدون سرور اجرا میشود:
واردسازی دیتابیس.اپن سرچ
تابع ریشه:
پاسخ سرور := `{"took":3,"timed_out":false,"hits":{"total":{"value":2,"relation":"eq"},"max_score":2.1,"hits":[{"_index":"books","_id":"1","_score":2.1,"_source":{"title":"Shahnameh","author":"Ferdowsi"},"highlight":{"title":["<em>Shahnameh</em>"]}},{"_index":"books","_id":"7","_score":0.8,"_source":{"title":"Tales from the Shahnameh","author":"Anonymous"}}]}}`
ر := اپن سرچ.تجزیه جستجو(پاسخ سرور)
سرچاپ ر.total، "یافته در"، ر.took، "میلیثانیه"
هر ه در ر.hits:
سرچاپ ه.id، ه.score، اپن سرچ.مسیر جیسون(ه.source، "title")، "از"، اپن سرچ.مسیر جیسون(ه.source، "author")
پایان
سرچاپ اپن سرچ.مسیر جیسون(ر.hits.بگیر(0).highlight، "title.0")
پایانشمردن با تجمیع
تجمیعها یکجا دربارهی همهی تطبیقها پاسخ میدهند: هر نویسنده چند کتاب دارد، میانگین سال چیست، نمودار ماهانه چه شکلی است. آنها را با افزودن تجمیع به درخواست بیفزایید، اگر فقط عددها را میخواهید size را ۰ بگذارید و پاسخ را با سطلها برای گروهها و مقدار تجمیع برای مقدارهای تکی بخوانید. خوانندههای دیگر بقیه را پوشش میدهند: فیلد تجمیع برای یک عدد از نتیجهی stats و صدک تجمیع برای صدکها.
واردسازی دیتابیس.اپن سرچ
تابع ریشه:
ناپایا تجمیعها := اپن سرچ.افزودن تجمیع("{}"، "authors"، اپن سرچ.تجمیع واژهها("author"، 3))
تجمیعها = اپن سرچ.افزودن تجمیع(تجمیعها، "avg_year"، اپن سرچ.تجمیع میانگین("year"))
سرچاپ اپن سرچ.بدنه جستجو(اپن سرچ.درخواست جستجو { size = 0، aggs = تجمیعها })
پاسخ سرور := `{"took":2,"hits":{"total":{"value":40,"relation":"eq"},"hits":[]},"aggregations":{"authors":{"buckets":[{"key":"Saadi","doc_count":12},{"key":"Rumi","doc_count":9},{"key":"Hafez","doc_count":7}]},"avg_year":{"value":1265.5}}}`
ر := اپن سرچ.تجزیه جستجو(پاسخ سرور)
هر ب در اپن سرچ.سطلها(ر.aggregations، "authors"):
سرچاپ ب.key، ب.doc_count
پایان
سرچاپ "میانگین سال:"، اپن سرچ.مقدار تجمیع(ر.aggregations، "avg_year")
پایانگذر از همهچیز
from و size برای صفحههای نخستی که آدمها میبینند خوب است، ولی نه برای گذشتن از کل یک نمایه. برای آن با ایجاد نقطه زمانی یک نقطهی زمانی باز کنید که نمایی از نمایه را ثابت نگه میدارد، با یک فیلد مرتب کنید و هر صفحهی بعد را با search_after برابر مقدارهای مرتبسازی آخرین یافته بخواهید. صفحهی خالی یعنی تمام؛ نقطهی زمانی را با حذف نقطه زمانی ببندید.
واردسازی دیتابیس.اپن سرچ
تابع ریشه:
ک := اپن سرچ.اتصال("http://localhost:9200")
نقطه := اپن سرچ.ایجاد نقطه زمانی(ک، "books"، "2m")
ناپایا درخواست := اپن سرچ.درخواست جستجو { query = اپن سرچ.تطبیق همه()، size = 100، pit_id = نقطه، pit_keep_alive = "2m" }
درخواست.sort.بیفزا(اپن سرچ.مرتب صعودی("year"))
ناپایا دیدهشده := 0
ناپایا صفحه := اپن سرچ.جستجو با درخواست(ک، ""، درخواست)
تا صفحه.hits.طول() > 0:
دیدهشده += صفحه.hits.طول()
درخواست.search_after = صفحه.hits.بگیر(صفحه.hits.طول() - 1).sort
صفحه = اپن سرچ.جستجو با درخواست(ک، ""، درخواست)
پایان
اپن سرچ.حذف نقطه زمانی(ک، نقطه)
سرچاپ دیدهشده، "کتاب"
پایانSQL و PPL
اپن سرچ به SQL هم پاسخ میدهد. اتصال عمومی یک کارخواه را به اتصال دیتابیس تبدیل میکند، همان رابطی که موتورهای SQL دارند، پس پرسمان عدد و حلقهی ردیفهای بستهی دیتابیس بیتغییر کار میکنند. PPL زبان پرسمان لولهای اپن سرچ است که برای گزارشها کارآمد است؛ پی پی ال ردیفها را به همان شکل برمیگرداند. نتیجههای بلند صفحه به صفحه میآیند و ردیفها هر چه پیش بروید صفحهی بعد را میگیرند.
واردسازی دیتابیس
واردسازی دیتابیس.اپن سرچ
تابع ریشه:
ک := اپن سرچ.اتصال("http://localhost:9200")
رابط := اپن سرچ.اتصال عمومی(ک)
سرچاپ دیتابیس.پرسمان عدد(رابط، "SELECT COUNT(*) FROM books WHERE year > 1200")
ردیفها := رابط.پرسمان("SELECT author, COUNT(*) AS n FROM books GROUP BY author ORDER BY n DESC")
تا ردیفها.بعدی():
سرچاپ ردیفها.متن(0)، ردیفها.عدد صحیح(1)
پایان
ردیفها.بستن()
پ := اپن سرچ.پی پی ال(ک، "source=books | where year > 1200 | stats count() by author")
تا پ.بعدی():
سرچاپ پ.متن(1)، پ.عدد صحیح(0)
پایان
پ.بستن()
پایانجستجوی برداری
یک فیلد knn_vector یک بردار جاسازی نگه میدارد، فهرستی از عددها که یک متن یا تصویر را در فضا جایی میگذارد که چیزهای مشابه نزدیک هم باشند. بدنه نمایه همسایهیابی نمایهای با چنین فیلدی میسازد و نزدیکترین همسایهها سندهای نزدیک به یک بردار را پیدا میکند. عصبی و ترکیبی یک گام جلوتر میروند و میگذارند اپن سرچ بردار را با مدلی که خودش میزبانی میکند از متن بسازد.
واردسازی دیتابیس.اپن سرچ
تابع ریشه:
ک := اپن سرچ.اتصال("http://localhost:9200")
روش درخواست := اپن سرچ.روش همسایهیابی { engine = "lucene"، space_type = "cosinesimil" }
ناپایا افزوده := وکتور {} برگردان وکتور<رشته>
افزوده.بیفزا(اپن سرچ.ویژگی("name"، "text"))
اپن سرچ.ایجاد نمایه(ک، "places"، اپن سرچ.بدنه نمایه همسایهیابی("embedding"، 3، روش درخواست، افزوده))
اپن سرچ.نمایهسازی(ک، "places"، "1"، `{"name":"Naderi Cafe","embedding":[0.1,0.9,0.2]}`)
اپن سرچ.تازهسازی(ک، "places")
ناپایا ز := وکتور {} برگردان وکتور<اعشار۶۴>
ز.بیفزا(0.1)
ز.بیفزا(0.8)
ز.بیفزا(0.3)
ر := اپن سرچ.جستجو(ک، "places"، اپن سرچ.نزدیکترین همسایهها("embedding"، ز، 3))
هر ه در ر.hits:
سرچاپ ه.id، ه.score، ه.source
پایان
پایانوقتی چیزی خراب میشود
هیچ فراخوانی برنامهی شما را متوقف نمیکند. فراخوانی ناموفق نادرست، ۱- یا نتیجهی خالی برمیگرداند و کارخواه آنچه رخ داده را به یاد میسپارد: وضعیت وضعیت HTTP آخرین درخواست است (وقتی پاسخی نیامد ۰)، خطا پیامی خواناست و نوع خطا و علت خطا همان چیزیاند که اپن سرچ گفته، مثل version_conflict_engine_exception. نشانی نادرست هنگام ساختن کارخواه گرفته میشود و معتبر آن را میگوید.
واردسازی دیتابیس.اپن سرچ
تابع ریشه:
ناپایا تنظیم := اپن سرچ.پیکربندی پیشفرض()
تنظیم.nodes = وکتور {} برگردان وکتور<رشته>
تنظیم.nodes.بیفزا("http://127.0.0.1:1")
تنظیم.max_retries = 0
ک := اپن سرچ.کارخواه تازه(تنظیم)
سرچاپ "پیکربندی معتبر:"، اپن سرچ.معتبر(ک)
د := اپن سرچ.دریافت(ک، "books"، "1")
سرچاپ "پیدا شد:"، د.found
سرچاپ "وضعیت:"، اپن سرچ.وضعیت(ک)
سرچاپ "خطا:"، اپن سرچ.خطا(ک)
خراب := اپن سرچ.اتصال("ftp://example.com")
سرچاپ "پیکربندی معتبر:"، اپن سرچ.معتبر(خراب)، "|"، اپن سرچ.خطا(خراب)
پایانگام بعد
راهنمای بستهی اپن سرچ همهی تابعها را با نمونه فهرست میکند، از جمله الگوهای نمایه، نامهای مستعار، تصویر لحظهای، خط لولهی ورودی، سیاستهای ISM، افزونهی امنیت، هشدار و ML Commons. برای هر چه بسته تابعی برایش ندارد، انجام هر درخواستی را که بنویسید میفرستد.