Sql-Server

SQL Server 全文搜尋 - .rtf 文件被 rtf 標籤錯誤地索引

  • August 4, 2020

varbinary(max)我已經在我的 SQL Server DB 的列上設置了全文索引。我指定了一個類型列,其中包含文件的副檔名,例如“.doc”、“.pdf”等。

但是,我注意到,當索引任何 .rtf 文件時,SQL 會包含文件中的所有元資訊(例如 RTF 標記“listoverridecount0”)。

這使索引膨脹很多,也意味著搜尋將匹配這些標籤(即我可以搜尋“listoverridecount0”並返回每個.rtf)。

.rtf 的 iFilter 是否有任何理由不會刪除 RTF 標籤?

當我執行這個:

SELECT * FROM sys.fulltext_document_types WHERE document_type = '.rtf';

我明白了:

document_type  .rtf                                     
class_id       C7310720-AC80-11D1-8DF3-00C04FB6EF4F 
path       c:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Binn\msfte.dll
version        12.0.6828.0
manufacturer   Microsoft Corporation

我已經送出了Microsoft Connect 錯誤,因為我似乎無法找到任何解決方法。這可能是 a) RTF iFilter 未刪除標籤時出錯,或 b) 全文索引問題。

我的 SQL Server 版本是:

微軟 SQL Server 2012 (SP1) - 11.0.3393.0 (X64)
2013 年 10 月 25 日 19:04:40
版權所有 (c) 微軟公司
Windows NT 6.2(內部版本 9200:)(管理程序)上的開發人員版(64 位)

我可以重現這個,但我認為你有幾個選擇:

  • 不要太擔心全文索引膨脹。沒有多少人會搜尋“rtf1”、“pard”或“wmetafile0”,我認為它不會對你的表現產生太大影響。如果有人在搜尋“紅色”或“藍色”,您可能會遇到奇怪的衝突,但我認為這是相當低的風險。
  • 清理字元串,例如使用我從這裡複製的 RegEx 。似乎做得很好。將其儲存在一個單獨的表中,其中帶有 stream_id(這是文件的唯一 id)和該表的全文索引:
-- Convert rtf to plain text
SELECT 
   CAST( file_stream AS VARCHAR(MAX) ) original
   , MDS.mdq.RegExReplace( 
       CAST( file_stream AS VARCHAR(MAX) ), 
       '\{\*?\\[^{}]+}|[{}]|\\\n?[A-Za-z]+\n?(?:-?\d+)?[ ]?', '', 1 )
FROM dbo.Documents
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Text;
using System.Linq;  // for TakeWhile

public partial class UserDefinedFunctions
{
   [Microsoft.SqlServer.Server.SqlFunction]
   public static SqlChars rtfToText( SqlChars inputRtf )
   {
       // RTF function lifted from here and adapted for SQL CLR:
       // https://stackoverflow.com/questions/23277178/richtextbox-throws-outofmemory-on-azure

       bool slash = false; //indicates if backslash followed by the space
       bool figure_opened = false; //indicates if opening figure brace followed by the space
       bool figure_closed = false; //indicates if closing brace followed by the space
       bool first_space = false; //the else spaces are in plain text and must be included to the result

       if (inputRtf.Length < 4) return new SqlChars ( string.Empty );

       int i = 0;
       i = inputRtf.ToString().IndexOf("\\pard");
       if (i < 1) return new SqlChars(string.Empty);

       var builder = new StringBuilder();
       for (int j = i; j < inputRtf.Length - 1; j++)
       {
           char ch = inputRtf[j];
           char nextCh = inputRtf[j + 1];

           if (ch == '\\' && nextCh == 'p') // appends \n if \pard, except for first
           {
               if (j > i && j < inputRtf.Length - 4)
               {
                   string fiveChars = inputRtf.ToString().Substring(j, 5);
                   if (fiveChars.Equals("\\pard"))
                   {
                       builder.Append("\n");
                   }
               }
           }

           if (ch == '\\' && nextCh == 'u') // to deal correctly with special characters
           {
               string fourChars = inputRtf.ToString().Substring(j + 2, 4);
               string digits = new string(fourChars.TakeWhile(char.IsDigit).ToArray());
               char specialChar = (char)int.Parse(digits);
               builder.Append(specialChar);
               j += digits.Length + 5;
               continue;
           }

           if (ch == '\\' && nextCh == '{') // if the text contains symbol '{'
           {
               slash = false;
               figure_opened = false;
               figure_closed = false;
               first_space = false;
               builder.Append('{');
               j++;
               continue;
           }
           else if (ch == '\\' && nextCh == '}') // if the text contains symbol '}'
           {
               slash = false;
               figure_opened = false;
               figure_closed = false;
               first_space = false;
               builder.Append('}');
               j++;
               continue;
           }
           else if (ch == '\\' && nextCh == '\\') // if the text contains symbol '\'
           {
               slash = false;
               figure_opened = false;
               figure_closed = false;
               first_space = false;
               builder.Append('\\');
               j++;
               continue;
           }
           else if (ch == '\\') // we are looking at the backslash
           {
               first_space = true;
               slash = true;
           }
           else if (ch == '{')
           {
               first_space = true;
               figure_opened = true;
           }
           else if (ch == '}')
           {
               first_space = true;
               figure_closed = true;
           }
           else if (ch == ' ')
           {
               slash = false;
               figure_opened = false;
               figure_closed = false;
           }

           if (!slash && !figure_opened && !figure_closed)
           {
               if (!first_space)
               {
                   builder.Append(ch);
               }
               else
               {
                   first_space = false;
               }
           }
       }

       // Return
       return new SqlChars(builder.ToString());
   }
};

我確信這不是世界上最高效的 CLR 函式,但它在我測試的大約 100 個文件上做得很好,有一些失敗了。

如果您在連接項目上聽到任何回音,請告訴我們。

我不確定您使用的是什麼版本的 MSSQL,但是這個連結有幫助嗎?SQL 過濾器它查看 SQL Server 中設置的各種過濾器,也許 .rtf 過濾器沒有正確載入?

引用自:https://dba.stackexchange.com/questions/57383