چرا سکان آکادمی؟
کوئری combined_fields در دسته‌بندی full-text

کوئری combined_fields در دسته‌بندی full-text

در قسمت قبلی به معرفی کوئری‌های match پرداختیم. یکی از انواع این کوئری‌ها کوئری multi_match است که برای تطبیق کلمات در چندین فیلد به کار می‌رود. دیدیم که یکی از حالات (type) این کوئری cross_fields است و برای مواردی کاربرد دارد که بخواهیم جستجوی term ها در تمامی فیلد‌ها به صورت یکپارچه صورت گیرد و گویی داده‌های تمامی فیلد‌های مورد نظر باهم ادغام شده باشد و term های مورد جستجو در کل داده‌ی ادغام شده تطبیق داده شود (به عنوان مثال جستجوی نام و نام خانوادگی در فیلد‌های first_name و last_name که در قسمت قبلی دیدیم!).

همچنین نحوه اعمال شروط و پارامتر‌ها (operator و minimum_should_match) در حالت cross_fields را با حالات best_fields و most_fields مقایسه کردیم و به طور کلی می‌توان گفت در حالت cross_fields شروط به صورت term-centric یا ترم محور و در حالات دیگر field-centric یا فیلد محور اعمال می‌شوند. این تفاوت در خصوص نحوه‌ی محاسبه‌ی امتیاز نیز صادق است

به عنوان مثال عبارت‌های "بهنام ایزدی" و "رضا بهنام" را درنظر بگیرید. با جستجوی "بهنام ایزدی" در حالات best_fields یا most_fields احتمالا "رضا بهنام" نیز در نتایج ظاهر شده (اگر براتون سوال هست چرا، حتما قسمت قبلی را مطالعه کنید!) و حتی ممکن است بالاتر از "بهنام ایزدی" قرار گرفته و امتیاز بیشتری بگیرد! دلیلش این است که پراکندگی "بهنام" در فیلد last_name بسیار نادر بوده و طبق الگوریتم Elasticsearch و فاکتور term frequency، اهمیت بالایی پیدا خواهد کرد که ممکن است اثر امتیاز آن بر ترکیب امتیاز "بهنام" در first_name و "ایزدی" در last_name غلبه کند:

GET some_index/_search
{
  "query": {
    "multi_match": {
      "query": "بهنام ایزدی",
      "type": "best_fields",
      "fields": [ "first_name", "last_name"]
    }
  }
}

 

اما در حالت cross_fields داده‌ی هردو فیلد باهم ادغام شده و دیگر پراکندگی پایین کلمه‌ی "بهنام" در فیلد last_name اثر سوء نخواهد داشت و قطعا در این حالت "بهنام ایزدی" به درستی امتیاز بالاتری نسبت به "رضا بهنام" خواهد گرفت:

GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "بهنام ایزدی",
      "type":       "cross_fields",
      "fields":     [ "first_name", "last_name" ],
      "operator":   "and"
    }
  }
}

 

در کوئری بالا پارامتر operator و مقدار and برای آن به درستی این نتیجه را خواهد داشت که هر دو term مدنظر حداقل در یکی از فیلد‌های مدنظر (یا به عبارتی در داده‌ی ادغام شده‌ی هردو فیلد) همزمان ظاهر شده باشند.

 

 نکته‌ی مهم: برای استفاده از حالت cross_fields، فیلد‌های مورد جستجو باید search_analyzer یکسان داشته باشند. به عبارتی کوئری cross_fields، فیلدهای با analyzer یکسان را گروه‌بندی کرده و داده‌ی آن‌ها را ادغام و سپس شروع به جستجو در هر گروه از داده‌های ادغام شده خواهد کرد. بنابراین اگر فیلد‌های مورد نظر هرکدام analyzer متفاوتی داشته باشند، در عمل این حالت تفاوتی با best_fields یا most_fields نخواهد داشت چرا که هر گروه شامل داده‌ی یک فیلد بوده و نهایتا در عمل جستجو در هر فیلد‌ به صورت مجزا انجام خواهد شد. البته می‌توان با استفاده از پارامتر analyzer در کوئری جستجو، مقدار analyzer جستجو را برای همه‌ی فیلد‌ها مشترک تنظیم کرده و کوئری را مجبور به ادغام داده‌ی فیلد‌ها کرد. مانند مثال زیر:

GET some_index/_search
{
  "query": {
   "multi_match": {
      "query": "علیرضا وفایی صدر",
      "type": "cross_fields",
      "analyzer": "standard", 
      "fields": ["first_name", "last_name"]
    }
  }
}

 

حال که در خصوص نحوه‌ی امتیازدهی در حالت cross_fields از کوئری multi_match صحبت کردیم، موقع آن رسیده که به نقص آن نیز اشاره کنیم. طبق مستندات رسمی Elasticsearch حالت cross_fields نتایج آماری فیلد‌ها را با یکدیگر ادغام کرده و در زمان محاسبه امتیاز به روش BM25 ممکن است امتیازها به شکل درستی تولید نشوند (مثلا ممکن است منفی شوند!) از این رو Elasticsearch توصیه کرده است که این نوع کوئری برای فیلد‌هایی با طول متن کوتاه و پارامتر boost با مقدار 1 استفاده شود. Elasticsearch به عنوان جایگزین در نسخه‌ی 7.13 یک کوئری به نام combined_fields در دسته‌بندی full-text معرفی کرده است که نحوه‌ی کارکرد آن دقیقا مشابه حالت cross_fields در multi_match بوده اما نسبت به آن بهبود داشته و نقص گفته شده نیز در آن برطرف شده است.

در مثال زیر یک نمونه از کوئری combined_fields را مشاهده می‌کنید. در این کوئری نیز مشابه کوئری‌های multi_match پارامتر‌های operator، fields، minimum_should_match و query قابل استفاده می‌باشند:

GET sample_index/_search
{
  "query": {
    "combined_fields": {
      "query": "database systems",
      "fields": [ "title", "body"],
      "operator": "and"
    }
  }
}
دوره در حال تکمیل است ... rocket