کوئری‌های query string در دسته‌بندی full-text

کوئری‌های query string در دسته‌بندی full-text

در ادامه بررسی کوئری‌های دسته‌بندی full-text، به query string ها می‌رسیم. دقت کنید کوئری query string در Elasticsearch ارتباطی با مفهوم URL query string ندارد!

Query string در Elasticsearch یک کوئری به فرمت رشته متنی(string) است که توسط یک parser تفسیر شده و در لایه‌ی درونی تبدیل به کوئری‌های مختلفی از جمله انواع کوئری match یا bool شده و سپس درخواست جستجوی مورد نظر انجام می‌شود (هنوز در خصوص کوئری bool صحبت نکردیم و در قسمت‌های آینده به آن خواهیم پرداخت!)

در query string پس از تفسیر متن کوئری، کوئری‌های مختلفی از آن استخراج شده سپس توسط عملگر‌ها (مثلا AND و OR) عبارات کوئری‌ها باهم ادغام شده و در نهایت مقادیر مورد جستجو در فیلد‌های از نوع text، توسط analyzer آن فیلد و یا analyzer خاصی که در کوئری تعیین شده باشد، تجزیه و تحلیل خواهند شد. به مثال زیر توجه کنید:

عبارت query string در مثال بالا به دو کوئری match تفسیر شده و از طریق کوئری bool و منطق OR با یکدیگر ادغام شده‌اند (جزییات کوئری bool را در قسمت‌های آینده بررسی خواهیم کرد اما در خصوص مثال بالا این مورد را در نظر بگیرید که دو کوئری match با منطق OR اجرا می‌شوند یعنی کافی است حداقل یکی از آن‌ها تطبیق داده شود و نیازی به تطبیق هر دو نیست!)

ممکن است سوالی برای شما ایجاد شود که چه نیازی به استفاده از کوئری query string است در حالیکه نتیجه‌ی نهایی را می‌شود با ادغام دو کوئری match به دست آورد! این سوالی مهم و کلیدی است که در جواب باید گفت query string برای مواقعی کاربردی است که کوئری مورد نظر ما پیچیدگی بالایی در منطق ترکیب شروط و نوع تطبیق مقادیر داشته باشد (برخلاف مثال بالا که صرفا یک مثال ساده است!) و در صورتی که بخواهیم با ساختار کوئری‌هایی مثل wildcard ،bool ،multi_match ،match و ... آن را پیاده‌سازی کنیم، مجبور به نوشتن کوئری طولانی‌تر و تودرتو هستیم که خوانایی مناسبی نداشته و یا شاید به راحتی قابل پیاده سازی نباشد. در این موارد استفاده از query string به دلیل خواناتر بودن فرمت آن مناسب است.

نکته مهم: به دلیل تفسیر متن query string، ممکن است خطاهای نحوی (syntax error) در متن وجود داشته باشد که Elasticsearch نسبت به این خطاها حساس بوده و آن‌ها را نادیده نخواهد گرفت! از این رو توصیه می‌شود از query string برای جستجوهایی که مقادیر ورودی آن‌ از یک search box گرفته می‌شود، استفاده نشود!

برای استفاده از query string از search API می‌توان به صورت زیر استفاده کرد:

GET cities/_search
{
  "query": {
    "query_string": {
      "query": "(اصفهان AND نصف AND جهان) OR (اصفهان)",
      "default_field": "city_description"
    }
  }
}

همانطور که می‌بینید، در کلید query داخل query string می‌توان متن اصلی کوئری مورد نظر را قرار داد. در خصوص قواعدی که برای نوشتن متن کوئری می‌توان استفاده کرد جلوتر صحبت خواهیم کرد.

مهم‌ترین پارامترهای query string

در سطح اول مقدار کلید query string، مشابه سایر کوئری‌هایی که تا الان بررسی کرده‌ایم می‌توان از پارامتر‌هایی جهت کنترل کوئری استفاده کرد که مهم‌ترین آن‌ها به شرح زیر هستند:

  • query: متن اصلی کوئری که تفسیر خواهد شد در این کلید قرار می‌گیرد. استفاده از این کلید اجباری است.
  • default_field: نام فیلد پیش‌فرض جهت جستجو زمانی که هیچ فیلدی در متن query قید نشده باشد. مقدار پیش‌فرض آن * به معنای تمامی فیلد‌ها است. استفاده از این کلید اختیاری است.
  • analyzer: نام analyzer مورد استفاده برای تجزیه‌ی مقادیر در کوئری. استفاده از این پارامتر اختیاری است و در صورت عدم استفاده از آن، به صورت پیش‌فرض از analyzer تعیین‌شده در mapping هر فیلد استفاده خواهد شد و در صورتی که از default_field استفاده شود از analyzer این فیلد استفاده خواهد شد.
  • boost: مشابه کارکرد این پارامتر در کوئری‌هایی که قبلا بررسی کرده‌ایم، این پارامتر برای ضریب‌دهی به امتیاز نتایج حاصل از کوئری استفاده خواهد شد.
  • default_operator: عملگر پیش‌فرض برای ترکیب شروط حاصل از term های تولید شده از متن مورد جستجو را تعیین می‌کند. به صورت پیش‌فرض مقدار OR دارد به این معنی که عبارت "اصفهان نصف جهان" به صورت "اصفهان OR نصف OR جهان" مورد تطبیق خواهد بود. مقادیر مجاز برای آن AND و OR می‌باشد.
  • fields: آرایه از نام فیلد‌های موردنظر برای جستجو که مقدار پیش‌فرض آن * به معنای تمامی فیلد‌ها می‌باشد.
  • fuzziness: کارکرد این پارامتر برای تطبیق تقریبی مقادیر مورد‌جستجو است که توضیحات آن در کوئری‌های قبلی داده شده است.
  • minimum_should_match: تعیین کننده‌ی حداقل عبارت‌های شرطی در کوئری‌های تولید‌شده که باید تطبیق داده شوند تا یک document در نتایج ظاهر شود!

نکته: این پارامتر در query string ممکن است عملکردی پیچیده‌تر داشته باشد. به مثال‌های زیر توجه کنید:

در ساده‌ترین حالت، query string متن کوئری را بر مبنای هر عملگر در مقدار مورد جستجو، به کوئری‌هایی تبدیل می‌کند. کوئری‌های به دست آمده حاصل از تفسیر query string، توسط عبارت‌های should (با جزییات این عبارت‌ها در کوئری bool در آینده بیشتر آشنا خواهیم شد!)  باهم ترکیب می‌شوند و پارامتر minimum_should_match تعیین می‌کند چه تعداد از این should ها باید انطباق داشته باشند. برای مثال نمونه زیر را درنظر بگیرید:

GET sample_index/_search
{
  "query": {
    "query_string": {
      "fields": [
        "counter"
      ],
      "query": "یک دو سه",
      "minimum_should_match": 2
    }
  }
}

در مثال بالا عدد 2 برای پارامتر minimum_should_match بیانگر این منطق است که حداقل دو مورد از term های "یک"، "دو" و "سه" باید در فیلد title منطبق شوند!

_ به عنوان مثالی دیگر نمونه زیر را برای جستجو در چندین فیلد درنظر بگیرید:

GET sample_index/_search
{
  "query": {
    "query_string": {
      "fields": [
        "counter",
        "text"
      ],
      "query": "یک دو سه",
      "minimum_should_match": 2
    }
  }
}

 

در مثال بالا عبارات should به شکل زیر تفسیر خواهند شد:

 

به این معنا که هر گروه از term ها به صورت تفکیک‌شده در هریک از فیلد‌ها جستجو شده و شرط تطبیق حداقل دو مورد در هر گروه به تفکیک هر فیلد اعمال خواهد شد. به عبارتی حداقل دو term به صورت همزمان باید در حداقل یکی از فیلد‌ها ظاهر شود.

حال نکته‌ی جالب و البته کمی عجیب این است که اگر عملگر OR به صورت صریح در متن query نوشته شود، نوع عملکرد متفاوت خواهد شد:

GET sample_index/_search
{
  "query": {
    "query_string": {
      "fields": [
        "counter",
        "text"
      ],
      "query": "یک OR دو OR سه",
      "minimum_should_match": 2
    }
  }
}

در این حالت شروط به صورت زیر ترکیب می‌شوند:

به این معنا که باید حداقل دو مورد از term ها در ترکیب مقادیر دو فیلد counter و text ظاهر شده باشند! (به عبارتی الزامی ندارد حداقل دو term در حداقل یکی از فیلد‌ها به صورت همزمان ظاهر شود بلکه ممکن است یک term در یک فیلد و term دیگر در فیلد دیگر باشد!)

نکات مهم در خصوص شیوه‌ی نوشتن متن query

  • قواعد استفاده از نام فیلد‌ها در query: 

_ فیلد status شامل مقدار active باشد:

status:active

_ فیلد status شامل مقادیر active یا loaded باشد:

status:(loaded OR active)

_ مقدار فیلد status شامل عبارت(phrase) "shut down" باشد:

status:"shut down"

_ فیلد first name شامل مقدار "رضا" باشد (از کاراکتر \ برای تعیین space در نام فیلد استفاده شده است):

first\ name:رضا

_ هریک از فیلد‌های book.title یا book.text یا book.summary شامل مقدار "سکان‌آکادمی" باشد (از کاراکتر \ برای تعیین کاراکتر * استفاده شده است):

Book.\*:سکان‌آکادمی

_ فیلد title شامل هر مقداری غیر از خالی باشد:

_exists_:title
  • قواعد استفاده از wildcard در query:

کاراکتر‌های wildcard امکان اعمال روی هر term به صورت تفکیک‌شده را دارند. از کاراکتر ? برای جایگزینی یک کاراکتر و کاراکتر * برای جایگزینی هیچ یا هر تعداد کاراکتر می‌توان استفاده کرد. مثلا:

title:(so?kan academ*)
  • استفاده از بازه برای مقادیر فیلد:

بازه‌ها ممکن است برای مقادیر فیلد‌های تاریخ، عددی یا متنی استفاده شوند. بازه‌های باز با {min TO max} و بازه‌های بسته با [min TO max] تعیین می‌شوند. برای مثال:

date:[2021-11-01 TO 2021-12-30}
count:[10 TO *]

برای مواردی که یک طرف بازه نامحدود است (مشابه مثال آخر برای فیلد count)، می‌توان به شکل زیر نیز کوئری را نوشت:

count:>=10
  • عملگر‌های منطقی (علاوه بر AND و OR):

برای تعیین الزام وجود یا عدم وجود یک term در مقدار مورد جستجو، می‌توان از کاراکتر‌ + برای الزام وجود و کاراکتر – برای الزام عدم وجود استفاده کرد. مثلا:

text:(quick brown +fox -news)

کوئری بالا معادل با کوئری زیر است که با عملگر‌های AND و OR معادل‌سازی شده است:

text:(((quick AND fox) OR (brown AND fox) OR fox) AND NOT news)
  • گروه‌بندی:

می‌توان شروط مختلف را با استفاده از پرانتز گروه‌بندی کرده و ترتیب اثر آن‌ها را کنترل کرد. برای مثال:

title:(quick OR brown) AND fox

در این قسمت با کوئری query string در Elasticsearch آشنا شدیم که با وجود قابلیت‌های فراوان آن، باید در نظر داشت که استفاده از آن فقط برای مواردی که مقادیر جستجو و نیازمندی نوع جستجو به صورت کاملا مشخص و از پیش تعیین باشد، منطقی است و در اکثر مواقع می‌توان با نوشتن کوئری‌های دیگر نیازمندی مورد نظر را به دست آورد. همچنین Elasticsearch یک کوئری به نام simple query string نیز ارائه داده است که قواعد و کارکرد آن مشابه query string بوده اما روند تفسیر آن سختگیری query string را نداشته و در صورت بروز خطای نحوی، آن بخش از کوئری نادیده گرفته خواهد شد.