بررسی Semantic Search و FTS Table-valued functions
Semantic Search جزو تازه‌های SQL Server 2012 است و مقدمات نصب و فعال سازی آن‌را در قسمت اول بررسی کردیم.
توابع Predicates مختص به FTS مانند Contains و Freetext، تنها ردیف‌های متناظر با جستجوی انجام شده را باز می‌گردانند و رتبه‌ای به نتایج جستجو اعمال نمی‌گردد. برای مثال، مشخص نیست اولین ردیف بازگشت داده شده بهترین تطابق را با جستجوی انجام شده دارد یا بدترین نتیجه‌ی ممکن است. برای رفع این مشکل FTS table-valued functions معرفی شده‌اند. حاصل این‌ها یک جدول با دو ستون است. ستون اول کلید متناظر با جدول تطابق یافته بوده و ستون دوم، Rank نام دارد که بیانگر میزان مفید بودن و درجه‌ی اعتبار ردیف بازگشت داده شده‌است.
Semantic Search نیز به کمک سه table-valued functions پیاده سازی می‌شود. همچنین باید دقت داشت که تمام زبان‌های پشتیبانی شده توسط FTS در حالت Semantic Search پشتیبانی نمی‌شوند. برای بررسی این مورد، دو کوئری ذیل را اجرا نمائید:
 -- Full text Languages
SELECT *
FROM sys.fulltext_languages
ORDER BY name;

-- Semantic Search Languages
SELECT *
FROM sys.fulltext_semantic_languages
ORDER BY name;
GO

بررسی table-valued functions مختص به FTS

دو متد ویژه‌ی CONTAINSTABLE و FREETEXTTABLE خروجی از نوع جدول دارند؛ با ستون‌هایی به نام‌های key و rank. اگر قسمت ایجاد کاتالوگ FTS و ایندکس آن‌را بخاطر داشته باشید، در حین ایجاد ایندکس FTS می‌بایستی KEY INDEX PK_Documents را نیز ذکر کرد. کاربرد آن در همین table-valued functions است.
مقدار rank، عددی است بین 0 و 1000 که هر چقدر مقدار آن بیشتر باشد، یعنی نتیجه‌ای نزدیک‌تر، به عبارت جستجو شده، یافت گردیده‌است. باید دقت داشت که این عدد فقط در زمینه‌ی یک کوئری معنا پیدا می‌کند و مقایسه‌ی rank دو کوئری مختلف با هم، بی‌معنا است.
عملکرد CONTAINSTABLE بسیار شبیه به متد Contains است با این تفاوت که قابلیت‌های بیشتری دارد. برای مثال در اینجا می‌توان برای قسمتی از جستجو، وزن و اهمیت بیشتری را قائل شد و این حالت تنها زمانی معنا پیدا می‌کند که خروجی جستجو، دارای rank باشد.
متد FREETEXTTABLE نیز بسیار شبیه به FREETEXT عمل کرده و نسبت به CONTAINSTABLE بسیار ساده‌تر است. برای نمونه امکان تعریف وزن، formsof، near و غیره در اینجا وجود ندارد. به علاوه عملگرهای منطقی مانند and و or نیز در اینجا کاربردی نداشته و صرفا یک noise word درنظر گرفته می‌شوند.


چند مثال جهت بررسی عملکرد دو متد CONTAINSTABLE و FREETEXTTABLE

استفاده از متد CONTAINSTABLE
 -- Rank with CONTAINSTABLE
SELECT D.id, D.title, CT.[RANK], D.docexcerpt
FROM CONTAINSTABLE(dbo.Documents, docexcerpt,
N'data OR level') AS CT
 INNER JOIN dbo.Documents AS D
  ON CT.[KEY] = D.id
ORDER BY CT.[RANK] DESC;
چون متد CONTAINSTABLE خروجی از نوع table دارد، در قسمت from ذکر شده‌است.
این متد ابتدا نام جدول مورد بررسی را دریافت می‌کند. سپس ستونی که باید جستجو بر روی آن انجام شود و در ادامه عبارت جستجو شونده، مشخص می‌گردد. اگر این متد را به تنهایی اجرا کنیم:
 SELECT * FROM CONTAINSTABLE(dbo.Documents, docexcerpt, N'data OR level')
همانطور که عنوان شد، صرفا یک سری ردیف اشاره کننده به id و rank را بازگشت می‌دهد. به همین جهت join نوشته شده‌است تا بتوان رکوردهای اصلی را نیز در همینجا به همراه rank متناظر، نمایش داد.


استفاده از متد FREETEXTTABLE

 -- Rank with FREETEXTTABLE
SELECT D.id, D.title, FT.[RANK], D.docexcerpt
FROM FREETEXTTABLE (dbo.Documents, docexcerpt,
N'data level') AS FT
 INNER JOIN dbo.Documents AS D
  ON FT.[KEY] = D.id
ORDER BY FT.[RANK] DESC;
کلیات عملکرد متد FREETEXTTABLE بسیار شبیه است به متد CONTAINSTABLE؛ با این تفاوت که ساده‌تر بوده و بسیاری از قابلیت‌های پیشرفته و سفارشی CONTAINSTABLE را به صورت خودکار و یکجا اعمال می‌کند. به همین جهت دقت آن، اندکی کمتر بوده و عمومی‌تر عمل می‌کند.
در اینجا اگر نیاز باشد تا تعداد نتایج را شبیه به کوئری‌های top n محدود نمود، می‌توان از پارامتر عددی بعدی که برای نمونه به 2 تنظیم شده‌است، استفاده کرد:
 -- Rank with FREETEXTTABLE and top_n_by_rank
SELECT D.id, D.title, FT.[RANK], D.docexcerpt
FROM FREETEXTTABLE (dbo.Documents, docexcerpt,
N'data level', 2) AS FT
 INNER JOIN dbo.Documents AS D
  ON FT.[KEY] = D.id
ORDER BY FT.[RANK] DESC;
در این کوئری تنها 2 ردیف بازگشت داده می‌شود.

تعیین وزن و اهمیت کلمات در حال جستجو

 -- Weighted terms
SELECT D.id, D.title, CT.[RANK], D.docexcerpt
FROM CONTAINSTABLE
(dbo.Documents, docexcerpt,
N'ISABOUT(data weight(0.8), level weight(0.2))') AS CT
 INNER JOIN dbo.Documents AS D
  ON CT.[KEY] = D.id
ORDER BY CT.[RANK] DESC;
با استفاده از واژه کلیدی ISABOUT، امکان تعیین وزن، برای واژه‌های در حال جستجو ممکن می‌شوند. در این کوئری اهمیت واژه data بیشتر از اهمیت واژه level تعیین شده‌است.


انجام جستجو‌های Proximity
 -- Proximity term
SELECT D.id, D.title, CT.[RANK]
FROM CONTAINSTABLE (dbo.Documents, doccontent,
N'NEAR((data, row), 30)') AS CT
 INNER JOIN dbo.Documents AS D
  ON CT.[KEY] = D.id
ORDER BY CT.[RANK] DESC;
GO
در اینجا مانند متد CONTAINS، امکان انجام جستجوهای Proximity نیز وجود دارد. برای مثال در کوئری فوق به دنبال رکوردهایی هستیم که در آن‌ها واژه‌های data و row وجود دارند، با فاصله‌ای کمتر از 30 کلمه.


بررسی Semantic Search key valued functions

متد SEMANTICKEYPHRASETABLE کار بازگشت واژه‌های کلیدی آنالیز شده توسط FTS را انجام داده و جدولی حاوی 4 ستون را باز می‌گرداند. این چهار ستون عبارتند از:
- column_id: شماره ستون واژه کلیدی یافت شده‌است. تفسیر آن نیاز به استفاده از تابع سیستمی COL_NAME دارد (مانند مثال زیر).
- document_key: متناظر است با کلید اصلی جدولی که بر روی آن کوئری گرفته می‌شود.
- keyphrase: همان واژه کلیدی است.
- score: رتبه‌ی واژه کلیدی است در بین سایر واژه‌هایی که بازگشت داده شده و عددی است بین صفر تا یک.

مثالی از آن‌را در ادامه ملاحظه می‌کنید:
 -- Top 100 semantic key phrases
SELECT TOP (100)
 D.id, D.title,
 SKT.column_id,
 COL_NAME(OBJECT_ID(N'dbo.Documents'), SKT.column_id) AS column_name,
 SKT.document_key,
 SKT.keyphrase, SKT.score
FROM SEMANTICKEYPHRASETABLE
(dbo.Documents, doccontent) AS SKT
 INNER JOIN dbo.Documents AS D
  ON SKT.document_key = D.id
ORDER BY SKT.score DESC;


در متد جدولی SEMANTICKEYPHRASETABLE، ابتدا جدول مورد نظر و سپس ستونی که نیاز است واژه‌های کلیدی آنالیز شده‌ی آن بازگشت داده شوند، قید می‌گردند. document_key آن به تنهایی شاید مفید نباشد. به همین جهت join شده‌است به جدول اصلی، تا بتوان رکوردهای متناظر را نیز بهتر تشخیص داد.
به این ترتیب مهم‌ترین واژه‌های کلیدی ستون doccontent را به همراه درجه‌ی اهمیت و رتبه‌ی آن‌ها، می‌توان گزارش گرفت.


متد SEMANTICSIMILARITYTABLE برای یافتن سندهای مشابه با یک سند مشخص بکار می‌روند؛ چیزی شبیه به گزارش «مقالات مشابه مطلب جاری» در بسیاری از سایت‌های ارائه‌ی محتوا. ستون‌های خروجی آن عبارتند از:
- source_column_id: شماره ستون منبع انجام کوئری.
- matched_column_id: شماره ستون سند مشابه یافت شده.
- matched_document_key: متناظر است با کلید اصلی جدولی که بر روی آن کوئری گرفته می‌شود.
- score: رتبه‌ی نسبی سند مشابه یافت شده.

 -- Documents that are similar to document 1
SELECT S.source_document_title,
 SST.matched_document_key,
 D.title AS matched_document_title,
 SST.score
FROM
 (SEMANTICSIMILARITYTABLE
  (dbo.Documents, doccontent, 1) AS SST
  INNER JOIN dbo.Documents AS D
ON SST.matched_document_key = D.id)
 CROSS JOIN
  (SELECT title FROM dbo.Documents WHERE id=1)
AS S(source_document_title)
ORDER BY SST.score DESC;



در این کوئری، اسناد مشابه با سند شماره 1 یافت شده‌اند. مبنای جستجو نیز ستون doccontent، جدول dbo.Documents است. از join بر روی matched_document_key و id جدول اصلی، مشخصات سند یافت شده را می‌توان استخراج کرد. کار CROSS JOIN تعریف شده، صرفا افزودن یک ستون مشخص به نتیجه‌ی خروجی کوئری است.
همانطور که در تصویر مشخص است، سند شماره 4 بسیار شبیه است به سند شماره 1. در ادامه قصد داریم بررسی کنیم که علت این شباهت چه بوده‌است؟


متد SEMANTICSIMILARITYDETAILSTABLE واژه‌های کلیدی مهم مشترک بین دو سند را بازگشت می‌دهد (سند منبع و سند مقصد). به این ترتیب می‌توان دریافت، چه واژه‌های کلیدی سبب شده‌اند تا این دو سند به هم شبیه باشند. ستون‌های خروجی آن عبارتند از:
- keyphrase: واژه‌ی کلیدی
- score: رتبه‌ی نسبی واژه‌ی کلیدی

 -- Key phrases that are common across two documents
SELECT SSDT.keyphrase, SSDT.score
FROM SEMANTICSIMILARITYDETAILSTABLE
(dbo.Documents, doccontent, 1,
doccontent, 4) AS SSDT
ORDER BY SSDT.score DESC;


در کوئری فوق قصد داریم بررسی کنیم چه واژه‌های کلیدی، سبب مشابهت سندهای شماره 1 و 4 شده‌اند و بین آن‌ها مشترک می‌باشند.
  • #
    ‫۱۰ سال و ۶ ماه قبل، شنبه ۲۳ فروردین ۱۳۹۳، ساعت ۱۸:۴۱
    با سلام
     SEMANTICSIMILARITYTABLE آیا برای متون فارسی هم کار می‌کند.
    من تست کردم نتیجه‌ای برای رکورهایی که با متون فارسی پر شدن بر نمی‌گردونه!  
    • #
      ‫۱۰ سال و ۶ ماه قبل، شنبه ۲۳ فروردین ۱۳۹۳، ساعت ۱۹:۳۱
      در ابتدای متن توضیح دادم: «همچنین باید دقت داشت که تمام زبان‌های پشتیبانی شده توسط FTS در حالت Semantic Search پشتیبانی نمی‌شوند. برای بررسی این مورد، دو کوئری ذیل را اجرا نمائید». فقط زبان‌هایی که حاصل گزارش زیر هستند Semantic Search در مورد آن‌ها صادق است:
      (زبان عربی در FTS پشتیبانی می‌شود؛ اما نه در Semantic Search) 
      SELECT * FROM sys.fulltext_semantic_languages ORDER BY name
      • #
        ‫۹ سال و ۹ ماه قبل، یکشنبه ۲۳ آذر ۱۳۹۳، ساعت ۲۱:۳۲
        ممنون جناب نصیری
        مدتها درگیر Semantec Search با در نظر گرفتن Stemming برای زبان فارسی با استفاده از لوسین و زبان جاوا بودم.
        سوالم این است که فیلترهای آفیس یا پی دی اف Adobe  هنگام fulltext search زبان فارسی رو پشتیبانی می‌کند؟ یعنی با استفاده از این فیلترها امکان جستجوی فارسی در فایلهای آفیس یا پی دی اف وجود دارد؟
        خود FTS در حالت جستجو در nvarchar(max) بصورت کامل از فارسی پشتیبانی می‌کند آیا امکان جستجوی فارسی در تایپهای varbinary(max) و فایلهای آفیس یا پی دی اف هم وجود دارد؟
          • #
            ‫۹ سال و ۹ ماه قبل، دوشنبه ۲۴ آذر ۱۳۹۳، ساعت ۱۸:۵۳
            ممنون جناب نصیری
            امکان دریافت شماره صفحه ای که عبارت مورد جستجو در آن یافت شده هم وجود دارد؟ بعبارتی آیا می‌توان به صفحه ای که عبارت جستجو شده در آن وجود دارد به نحوی دسترسی داشت؟
            تا جایی که من بررسی کردم در word اطلاعات صفحه (مثلا شروع یا پایان صفحه) در ساختار xml فایلهای word نگهداری نمی‌شود (بررسی شده با openxml) و هنگام نمایش در محیط Word صفحه ارایی انجام می‌گیرد. البته pdf را بررسی نکرده ام. در صورتیکه این امکان وجود داشته باشد جستجو در فایلهای داده مانند مجموعه آفیس و پی دی اف بسیار ساده خواهد بود. و نکته بعدی اینکه ما مدتها روی ریشه کلمات فارسی کار کرده ایم (wordnet فارسی برای کلمات متداول) و الگوریتمی هم برای آن تهیه کرده ایم که درصد خطای بسیار پایینی دارد  آیا امکان توسعه semantic language برای پشتیبانی از زبان فارسی وجود دارد؟
  • #
    ‫۹ سال و ۸ ماه قبل، پنجشنبه ۲۵ دی ۱۳۹۳، ساعت ۱۷:۴۴
    با سلام.
    من در جدول documents فیلد docexcerpt را در یک رکورد با Apple Iphone 6 64Gb و در رکوردی دیگر با Apple iphone 6 16Gb مقدار دهی کردم.
    دنبال این هستم که چطور میشود کوئری ای ساخت که در صورت که بر روی مقدار Apple Iphone 6 64Gb جستجو صورت گرفت هر دو رکورد را بیاورد ولی با rank‌ها مختلف.
    تا الان نتونستم به نتیجه ای برسم. و فقط یکی از رکورد‌ها را برمیگرداند.
    ممنون.
  • #
    ‫۹ سال و ۴ ماه قبل، شنبه ۹ خرداد ۱۳۹۴، ساعت ۱۶:۰۷
    با سلام
    راهی وجود داره که حین استفاده از Containstable تعیین کنیم که کلمات بصورت Case Sensitive   سرچ بشن یا خیر؟ علاوه بر اون، میشه عباراتی رو که شامل کلمه مورد جستجو هستند برگردوند؟ (با استفاده از مقداردهی عبارت در حال جستجو به شکل '"*name"' میشه startWith رو پیاده سازی کرد اما '"*name*"' بصورت Contains عمل نمی‌کنه)
    • #
      ‫۹ سال و ۴ ماه قبل، شنبه ۹ خرداد ۱۳۹۴، ساعت ۱۸:۴۰
      - کوئری‌های FTS به صورت پیش فرض case sensitive نیستند (^) و به این صورت طراحی شده‌است. البته اگر می‌خواهید این مورد همیشه صادق باشد، بهتر است عبارت جستجو شده را تبدیل به lower case یا uppercase کنید.
      Full-text queries are not case-sensitive. 
      For example, searching for "Aluminum" or "aluminum" returns the same results.
      مرجع

      - «شامل کلمه» یا partial words پشتیبانی نمی‌شود. چون tokenizer مربوط به FTS کوچکترین جزئی که دارد یک word است. این سؤال را هم بررسی کنید.
  • #
    ‫۹ سال و ۳ ماه قبل، سه‌شنبه ۲۶ خرداد ۱۳۹۴، ساعت ۱۸:۱۰
    سلام 
    راهی برای پیاده سازی Stopword در کوئری هایی که از ContainsTable  استفاده میکنند وجود داره؟
    من باید کوئری رو بصورت داینامیک بسازم و هر بار ممکنه StopWord‌ها متغیر باشند به همین خاطر نمیتونم از Stopword بصورت نرمال استفاده کنم. این روش رو پیدا کردم :
    Select Name , c.[rank]
    From CONTAINSTABLE (Users , Name, '"Ali*" AND NOT "Ali Reza"', 10)
    که با کلمه "Ali Reza" به عنوان یک Stopword برخورد میکنه. اما من میخوام لیستی از کلمات رو به کوئری بدم نه یک کلمه، و در کوئری بالا نمیشه از NOT IN استفاده کرد و با استفاده از این روش باید به ازای هر کلمه یک AND NOT اضافه کنم.
    میخواستم بدونم راهی برای این کار (غیر از بدست آوردن تمام کلمات و در نهایت فیلتر کردن شون) وجود داره؟
  • #
    ‫۷ سال و ۸ ماه قبل، یکشنبه ۲۶ دی ۱۳۹۵، ساعت ۱۵:۱۳
    با سلام ؛ زمانی که شما در گوگل سرچ انجام میدید گوگل جمله شما رو اصلاح کرده و به شما پیشنهاد میدهد سوال من اینکه چطور میتونم این موضوع رو تو سرچ‌های خودم پیاده سازی کنم به طور مثال کاربر من سرچ کرده سینمای آزادی الگوریتم اگه فرض باشه که الگوریتم اینجا کلمه بی ربطی و بخوام سرچمو اصلاح کنم به چه شکل میتونم اینکار رو انجام بدم
  • #
    ‫۷ سال و ۶ ماه قبل، شنبه ۲۸ اسفند ۱۳۹۵، ساعت ۱۲:۲۸
    با تشکر از مقاله مفیدتون
    یه سوال ممنون میشم اگه راهنمایی کنید
    به وسیله دستور isabout میخوام سرچ انجام بدم و از طریق این دستور قابلیت سرچ کلمه با تعیین حداکثر طول کاراکتر کلمه رو هم مشخص کنم به طور مثال اگه دیتای بافت و بافتنی و بافتنیها داشته باشیم و بافت رو سرچ کنیم بتونیم بافت و بافتنی رو به عنوان خروجی هم داشته باشیم ولی نمیخوام از طریق دستور * اینکار رو انجام بدم چون اگه دیتای بافتنیها داشته باشیم هم به عنوان خروجی بازیابی میشود