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

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

پس از بررسی کوئری‌های خانواده‌ی combined_fields ،match و query string نوبت به کوئری Intervals در دسته‌بندی full-text رسیده است. از این کوئری برای جستجو بر مبنای ترتیب و مجاورت term های مورد جستجو در document های هدف استفاده می‌شود.

کوئری Intervals از مجموعه‌ای از قوانین تطبیق (matching rules) که برای آن تعریف می‌شود استفاده و آن‌ها را روی term‌های فیلد مدنظر بررسی کرده تا نتایج قابل تطبیق را شناسایی کند.

ساختار کلی یک کوئری Intervals مشابه مثال زیر است:

GET some_index/_search
{
  "query": {
    "intervals": {
      <field_name>: {
        <matching_rule>: {
          …
          <optional_other_matching_rules>: [
            {
              …
            },
            {
              …
            }
          ]
        }
      }
    }
  }
}

طبق ساختار بالا، داخل کلید intervals در کلید query، نام فیلد مدنظر را نوشته (<field_name>) و سپس در object مقدار آن، شروع به تعریف قوانین تطبیق خواهیم کرد. همانطور که در ساختار بالا مشخص است، ممکن است یک قانون تطبیق، شامل قوانین دیگری نیز باشد!

برای درک بهتر تفاوت کارکرد این کوئری نسبت به سایر کوئری‌های دسته‌بندی full-text، به این مثال توجه کنید: فرض کنید بخواهیم document‌هایی را پیدا کنیم که شامل عبارت "تخصص مورد علاقه‌" بوده و در ادامه‌ی آن با هر تعداد فاصله(gap) عبارت "جستجوی پیشرفته" دیده شود. چیزی شبیه به ساختار زیر:

* [تخصص مورد علاقه] * [جستجوی پیشرفته] *

برای مثال متن "از جمله تخصص‌های مورد علاقه‌ی من، داده‌کاوی و پیاده‌سازی جستجوی‌های پیشرفته است!" نتیجه‌ی مطلوب این جستجو خواهد بود. اگرچه با استفاده از کوئری‌های غیر از intervals می‌توان نتایجی تقریبا نزدیک به هدف مطلوب داشته باشیم، اما توجه کنید هیچگاه متن "با یک جستجوی پیشرفته در بین گزینه‌های موجود، توانستم تخصص مورد علاقه‌ی خودم را پیدا کنم!" در بین نتایج یک کوئری intervals نخواهد بود و به عبارتی می‌توان نتایج دقیق‌تری را بر اساس ترتیب و مجاورت term ها با استفاده از این کوئری به دست آورد!

قوانین تطبیق در کوئری intervals

همانطور که بالاتر دیدیم، در کوئری Intervals پس از تعیین نام فیلد، شروع به تعریف قوانین خواهیم کرد. قوانین تطبیق شامل این موارد است:

  • match: متن جستجوشده را توسط analyzer تجزیه کرده و term های آن را در مقادیر فیلد موردنظر ضمن درنظر گرفتن پارامتر‌های تعیین‌شده تطبیق می‌دهد. پارامتر‌های این قانون عبارتند از:
    • query: شامل متن مورد جستجو
    • max_gaps: بیشترین تعداد مجاز فاصله بین term های متن مورد جستجو. مقدار پیش‌فرض آن -1 است به این معنی که فاصله‌ی بین term ها اهمیتی نداشته و اگر مقدار 0 برای آن درنظر بگیریم، تمامی term های متن باید کنار یکدیگر ظاهر شوند!
    • ordered: تعیین می‌کند آیا ترتیب term های متن مورد جستجو، اهمیت دارد یا خیر. پیش‌فرش مقدار false دارد.
    • analyzer: نام analyzer مدنظر برای تجزیه‌ی متن مورد جستجو. پیش‌فرض از analyzer فیلد تعیین شده در سطح اول کوئری استفاده می‌کند.
    • use_field: در صورتی که این پارامتر مقداردهی شود، متن مورد جستجو در مقادیر این فیلد تطبیق داده خواهد شد. به عبارتی این پارامتر جایگزین فیلد تعیین شده در سطح اول کوئری خواهد شد و از analyzer این فیلد برای تجزیه استفاده خواهد شد مگر اینکه پارامتر analyzer مقداردهی شده باشد.

مثالی از قانون match:

POST some_index/_search
{
  "query": {
    "intervals": {
      "my_field": {         
        "match": {
           "query": "دوره‌ی آنلاین",
           "max_gaps": 2,
           "ordered": true
        } 
      }
    }
  }
}
  • prefix: این قانون term هایی را تطبیق خواهد داد که مجموعه‌ای از کاراکتر‌ها در ابتدای آن‌ها ظاهر شده باشد. پارامتر‌های این قانون عبارتند از:
    • prefix: کاراکتر‌هایی که می‌خواهیم در ابتدای term ها ظاهر شده باشند.
    • analyzer: نام analyzer موردنظر برای نرمال‌سازی عبارت استفاده شده در پارامتر prefix. پیش‌فرض از analyzer فیلد تعیین‌شده در سطح اول کوئری استفاده خواهد کرد. دقت شود در این قانون analyzer تعیین‌شده تنها فرایند normalization را انجام خواهد داد و مقدار prefix تجزیه نخواهد شد.
    • use_field: در صورتی که این پارامتر مقداردهی شود، مقدار prefix در term های این فیلد تطبیق داده خواهد شد. به عبارتی این پارامتر جایگزین فیلد تعیین شده در سطح اول کوئری خواهد شد و از analyzer این فیلد برای تجزیه استفاده خواهد شد مگر اینکه پارامتر analyzer مقداردهی شده باشد.
  • wildcard: این قانون طبق یک الگوی wildcard شروع به تطبیق term ها خواهد کرد. تنها عملگر‌های مجاز wildcard در این قانون، عملگر ? برای تطبیق یک کاراکتر و عملگر * برای تطبیق هیچ یا چند کاراکتر می‌باشند. پارامترهای این قانون عبارتند از:
    • pattern: الگوی wildcard موردنظر برای جستجو
    • analyzer: مشابه پارامتر analyzer در قانون prefix
    • use_field: مشابه پارامتر use_field در سایر قوانین
  • fuzzy: نحوه‌ی عملکرد این قانون مشابه کوئری match با پارامتر fuzziness است که در قسمت‌های قبلی این فصل در مورد آن صحبت شد و برای تطبیق موارد مشابه با term های مورد جستجو به کار می‌رود. پارامتر‌های آن عبارتند از:
    • term: مقدار term مورد نظر برای جستجو
    • prefix_length: تعداد کاراکتر‌هایی از ابتدای term مورد جستجو که بدون تغییر باقی بماند (در واقع از بعد از آن تعداد کاراکتر قابلیت fuzziness اعمال شود!). مقدار پیش‌فرض 0 است.
    • transpositions: تعیین می‌کند آیا ویرایش کاراکتر‌ها شامل جابجایی دو کاراکتر مجاور نیز باشد یا خیر. مقدار پیش‌فرض true است.
    • fuzziness: تعیین بیشترین تعداد ویرایش مجاز برای تطبیق. برای یادآوری جزییات نحوه‌ی مقداردهی اینجا را مطالعه کنید.
    • analyzer: مشابه پارامتر analyzer در قانون prefix
    • use_field: مشابه پارامتر use_field در سایر قوانین
  • all_of: دربرگیرنده‌ی آرایه‌ای از قوانین تطبیق است. زمانی این قانون تطبیق یک document را تصدیق می‌کند که تمامی قوانین زیرمجموعه‌ی آن تطبیق داشته باشند. پارامتر‌های این قانون عبارتند از:
    • intervals: آرایه‌ای از قوانین کوئری intervals که باید همگی آن‌ها تطبیق داده شوند تا یک document در نتیجه نهایی ظاهر شود.
    • max-gaps: کارکرد این پارامتر مشابه کارکرد آن در قانون match است با این تفاوت که در قانون match این پارامتر بیانگر بیشترین فاصله‌ی مجاز بین term های تطبیق داده شده در عبارت جستجو‌شده بوده اما در این قانون بیانگر بیشترین فاصله‌ی بین term های تطبیق داده شده از تمامی قوانینی است که زیرمجموعه‌ی این قانون تعریف شده‌اند. 
    • ordered: تعیین می‌کند آیا term تطبیق داده شده در قوانین زیرمجموعه باید همان ترتیب تعریف قوانین ظاهر شده باشند یا خیر. پیش‌فرض مقدار آن false است.
  • any_of: دربرگیرنده‌ی آرایه‌ای از قوانین تطبیق است. زمانی این قانون تطبیق یک document را تصدیق می‌کند که حداقل یکی از قوانین زیرمجموعه‌ی آن تطبیق داشته باشند. پارامتر‌های این قانون عبارتند از:
    • intervals: آرایه‌ای از قوانین کوئری intervals که در صورت تطبیق هریک از آن‌ها document مربوطه در نتیجه نهایی ظاهر می‌شود.

توجه: قوانین all_of  ،match و any_of شامل پارامتری به نام filter نیز می‌باشند که برای فیلتر کردن موارد تطبیق داده شده در یک قانون استفاده می‌شود. این پارامتر خود شامل پارامتر‌های زیر است که با استفاده از آن‌ها می‌توان کوئری موردنظر برای فیلتر نتایج را ایجاد کرد:

  • after: کوئری مورد نظر برای فیلتر کردن نتایجی از کوئری قانون به نحوی که حداقل یکی از نتایج بعد از مورد تعیین شده در فیلتر after ظاهر شده باشد. برای مثال فرض کنید document زیر در ایندکس sample_index وجود داشته باشد (به مقدار فیلد text توجه کنید!):
POST sample_index/_doc
{
  "text": "first second third fourth"
}

اکنون فرض کنید بخواهیم document ای را جستجو کنیم که شامل کلمه‌ی "third" در فیلد text بوده و این کلمه، بعد از کلمه‌ی "first" ظاهر شده باشد (مانند مثال بالا). از کوئری زیر استفاده می‌کنیم:

GET sample_index/_search
{
  "query": {
    "intervals": {
      "text": {
        "match": {
         "query": "third",
         "filter": {
           "after": {
             "match": {
                "query": "first"
              }
            }
          }
        }
      }
    }
  }
}

document مثال زده شده در نتیجه کوئری بالا وجود خواهد داشت!

  • before: کوئری مورد نظر برای فیلتر کردن نتایجی از کوئری قانون به نحوی که حداقل یکی از نتایج قبل از مورد تعیین شده در فیلتر before ظاهر شده باشد. در مثال بالا اگر جای کلمات "first" و "third" عوض شده و به جای فیلتر after از فیلتر before استفاده شود، مجدد document مثال زده شده در نتیجه‌ی جستجو ظاهر خواهد شد (document ای مدنظر باشد که شامل کلمه‌ی "first" بوده و این کلمه قبل از "third" ظاهر شده باشد!)
  • contained_by: کوئری مورد نظر برای فیلتر کردن نتایج به نحوی که کوئری فیلتر contained_by شامل حداقل یکی از موارد کوئری قانون باشد. با توجه به مثال document بالا، نتیجه‌ی کوئری زیر شامل document مثال زده شده نخواهد بود (چون در این document کلمه‌ی "first" بین کلمات "second" و "fourth" قرار نگرفته است):
GET sample_index/_search
{
  "query": {
    "intervals": {
      "text": {
        "match": {
         "query": "first",
         "filter": {
           "contained_by": {
             "match": {
               "query": "second fourth"
              }
            }
          }
        }
      }
    }
  }
}
  • containing: کوئری مورد نظر برای فیلتر کردن نتایج به نحوی که حداقل یکی از نتایج شامل مورد تعیین شده در فیلتر containing باشد. برای مثال نتیجه‌ی کوئری زیر شامل document مثال زده شده خواهد بود:
GET sample_index/_search
{
  "query": {
    "intervals": {
      "text": {
        "match": {
         "query": "first third",
         "filter": {
           "containing": {
             "match": {
               "query": "second"
              }
            }
          }
        }
      }
    }
  }
}
  • not_containded_by: برعکس فیلتر contained_by عمل می‌کند.
  • not_containing: برعکس فیلتر containing عمل می‌کند.
  • overlapping: کوئری مورد نظر برای فیلتر کردن نتایج به نحوی که حداقل یکی از نتایج با مورد تعیین شده در فیلتر تداخل داشته باشد. برای مثال نتیجه‌ی کوئری زیر شامل docuemnt مثال زده شده خواهد بود:
GET sample_index/_search
{
  "query": {
    "intervals": {
      "text": {
        "match": {
         "query": "first third",
         "filter": {
           "overlapping": {
             "match": {
               "query": "second fourth"
              }
            }
          }
        }
      }
    }
  }
}
  • not_overlapping: برعکس فیلتر overlapping عمل می‌کند.

نکته: کوئری intervals همواره کوئری‌های ایجاد شده توسط قوانین تطبیق را به صورت حداقلی مورد تطبیق قرار می‌دهد تا مطمئن شود کوئری‌ها در مرتبه‌ی زمانی خطی (linear time) اجرا خواهند شد، به خصوص زمانی که از پارامتر max_gaps و یا filter استفاده شود. برای مثال کوئری زیر را در نظر بگیرید:

GET _search
{
  "query": {
    "intervals": {
     "text": {
       "all_of": {
         "intervals": [
           { "match" : { "query" : "گرگ" } }
           { "any_of": {
                "intervals": [
                    { "match": { "query": "درنده" } },
                    { "match": { "query": "درنده وحشی" } }
                ] 
           } },           
         ],
         "max_gaps": 0,
         "ordered": true
       }
     }
    }
  }
}

در کوئری مثال بالا، به طور عجیبی نتیجه‌ی کوئری با متن "گرگ درنده‌ وحشی" تطبیق پیدا نخواهد کرد چراکه در قانون any_of به دلیل تطبیق حداقلی، کوئری دوم که برای تطبیق عبارت "درنده وحشی" است از این جهت که term های آن در همان موقعیت کوئری قبل از آن (کوئری تطبیق عبارت "درنده") قرار دارند و فقط عبارت آن طولانی‌تر است، نادیده گرفته خواهد شد. بنابراین برای همچین مواردی استفاده از کوئری intervals به صورت صریح، مشابه مثال زیر توصیه می‌شود:

GET _search
{
  "query": {
    "intervals": {
     "text": {
       "any_of": {
         "intervals": [
           { "match": {
                "query": "گرگ درنده وحشی",
                "ordered": true,
                "max_gaps": 0 } },
           { "match": {
                "query": "گرگ درنده",
                "ordered": true,
                "max_gaps": 0 } }
          ]
       }
     }
    }
  }
}