65.9K
CodeProject 正在变化。 阅读更多。
Home

dexleapFTC,一个纯粹由存储过程组成的全文搜索引擎

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.35/5 (20投票s)

2006年7月17日

CPOL

8分钟阅读

viewsIcon

37383

downloadIcon

378

为所有 MS SQL Server 版本实现一个全文搜索引擎,全部使用存储过程。

Sample Image - dexleapFTC.gif

引言

要在 MS SQL Server 应用程序中执行全文搜索,通常有两种选择

  1. Select Something From SomeTable Where SomeText Like %SomePhrases%.
  2. 或者

  3. SQL Server 内置的全文搜索引擎。

第一种选择只能处理 8000 个字符长度的字段(4000 个 Unicode 字符)。当然,您可以将长文本拆分成多个 8K 字段,但问题在于 %SomePhrases% 模式。此查询总是会导致“蛮力”表扫描,当文本量增大时,这会非常慢。

第二种选择是更好的选择。但是,它仍然有两个问题

  1. SQL Server 桌面版 (MSDE) 没有这个内置引擎。
  2. 即使您的 SQL Server 版本有这个引擎,您也几乎无法控制它。

FTC 方法的目标是使 SQL Server 上的全文索引和搜索过程易于理解、易于实现,并让程序员完全控制该过程。

FTC 方法 - 如何索引

该方法由存储过程组成,并在所有 SQL Server 版本(包括 MSDE 和 Express 版本)上运行。

我们来看一个表

tbl_Content
ContentID CreateDate ContentText
4891 3/30/2006 The quick brown fox jumps over the lazy dog.

FTC 将内容文本逐个单词拆分,并将它们插入索引表中。无论文本有多大,单个单词总是由一个整数表示。这个整数是该单词的索引 ID。像“the”这样的停用词会被忽略。FTC 维护一个额外的表来处理这些停用词。

tbl_WordIndex
WordIndex IndexWord
1 quick
2 brown
3 fox
4 jumps
5 over
6 lazy
7 dog

tbl_IndexMap 链接 tbl_WordIndextbl_Content。该表的所有三个字段都是整数。这消除了最终表的大小并加快了搜索速度。为避免任何“蛮力表扫描(即,针对 tbl_Content 的“%%”模式查询),FTC 会记录每个单词在文本中的起始位置。这就像一个指针,因此 FTC 始终可以知道单词在原始文本中的确切位置。

tbl_IndexMap
WordIndex WordStartPosition ContentID
1 5 quick
2 11 brown
3 17 fox
4 21 jumps
5 27 over
6 36 lazy
7 41 dog

FTC 方法 - 如何分词

将短语中的单词拆分称为“词干提取”。大多数全文搜索引擎使用“词干提取符号”来提取基于拉丁语的语言,如英语、法语、西班牙语、意大利语和德语。词干提取符号包括空格、逗号、句号等。对于中文、日文和韩文(CJK)等远东语言,每个字符本身就是一个词干提取符号。

FTC 使用 UNICODE 范围进行词干提取。对于任何给定的字符串,FTC 都会逐个字符进行处理。如果字符在 0x0041 到 0x005a 之间,FTC 就知道它是一个有意义的拉丁字符。FTC 不会检查下一个字符,直到遇到任何非拉丁 UNICODE。当遇到非拉丁代码时,FTC 将从之前的字符构建一个单词。注意:范围 0x0041-0x005a 仅为示例。请访问 www.unicode.org 了解更多关于 UNICODE 的详细信息。

FTC 方法 - 如何搜索

假设您想在内容表中搜索精确短语“brown fox jumps”。

第一步:为此任务创建一个**临时**短语表

WordIndex PhraseWord WordRelativePosition
棕色 1
Fox 7
jumps 11

基本上,短语被拆分成单词,这些单词被插入到临时短语表中。“WordRelativePosition”是单词在短语中的位置。

第二步:为每个单词填充索引 ID(来自 tbl_WordIndex)

WordIndex PhraseWord WordRelativePosition
2 棕色 1
3 Fox 7
4 jumps 11

第三步:搜索...

目标是查找“tbl_IndexMap”中是否存在具有与短语表相同的单词索引和相同的相对位置的项。在临时短语表中,“brown”、“fox”、“jumps”分别具有位置“1”、“7”、“11”。在 tbl_IndexMap 中,“brown”、“fox”、“jumps”分别具有位置“11”、“17”、“21”。它们显然不同。但是,它们是相对相同的,它们都具有从单词“brown”开始的相同距离。因此,FTC 程序可以确定短语“brown fox jumps”在“content# 4891”中有精确的搜索结果。

表格

演示实现了这些表

  • tdatArchiveEntry -- 此表保存所有文本条目。
  • tdatWordIndex -- 此表处理唯一单词索引。每个单词都由一个整数表示。该整数是单词索引 ID。
  • tdatWordIndex2Text -- 此表映射“tdatWordIndex”和“tdatArchiveEntry”。
  • tdatWordIndexControl -- 这为长时间索引进程提供了立即停止的机会。
  • trefIndexStatus -- 这是一个引用表。它包含一个条目的四个可能状态:“不索引”、“准备索引”、“正在索引”、“已索引”。
  • trefWordNoise -- 这也是一个引用表。所有停用词都在这里,例如“the”、“a”、“this”、“that”等。

存储过程

演示实现了这些存储过程

  • up_EntryAdd
  • up_EntryAddStringChunk
  • up_EntryContent
  • up_EntryList
  • up_EntryReadyToIndex
  • up_EntryRemove
  • up_MaxPageNum
  • up_WordIndex
  • up_WordIndexControl
  • up_WordIndexProgress
  • up_WordIndexStartOver
  • up_WordSearch

命名非常直观。您可以根据名称判断其功能。最重要的两个 SP 是 up_WordIndexup_WordSearch

up_wordIndex 索引所有状态为“准备索引”的条目。索引后,这些条目的状态将变为“已索引”。此过程可能是一个长时间运行的任务。不需要输入参数。要停止长时间运行的任务,您可以将 tdatWordIndexControlynDoIndex 字段设置为“no”。这将立即停止正在运行的过程。

前端应用程序调用 up_wordSearch 来执行**精确**或**全部**搜索。“精确搜索”是指用户希望在文本中查找精确短语“jumps over the lazy dog”;“全部搜索”是指用户希望查找文本中同时包含“jumps,dog,fox”。

重要提示: 是的,您可以并发运行 up_WordIndex,当然,您可以随时按需运行它。但是,除非您正在开发一个单用户系统,否则这**是个坏主意**。

索引过程是一项繁重的长时间运行过程。

想象一个处理并发用户输入的 Web 应用程序。如果系统在用户提交其输入时启动索引过程,您猜会发生什么?

如果只有一个用户提交,您很幸运;10 个用户同时提交输入,系统会令人恼火地变慢一点;50 个用户同时提交,您的系统可能会崩溃。

一些**好的实践**应该是

  • 将索引过程放入调度程序并在批处理中运行。
  • 设计您的系统,使索引过程在系统空闲时运行。

前端技巧

演示的前端是一个带有 Web 浏览器控件的 C# WinForms 应用程序。

ActiveX 控件始终指向一个名为“ax.htm”的本地 HTML 文件。每当需要刷新时,FTC 程序将对“ax.htm”进行一些文件编辑,并触发 NavigateComplete2 事件来更新页面。

ax.htm 中的超链接(如“view”、“delete”、“page number”)被点击时,FTC 前端窗体会响应。这些超链接背后的技巧很简单,就是“about:WhatEverCommand”。

有许多其他文档教授如何使用 ATL 和 COM/ActiveX 处理程序来完成相同的任务,但没有一个像“about:”协议那样简单。

非官方性能基准测试

dexleapFTC 的性能取决于许多因素,如硬件配置、并发请求、索引质量和带宽等。

我设置了一个在线实时演示来测试它作为 Web 门户全文搜索引擎。在在线实时演示中,索引 450,000 个英文单词大约需要 400 秒。

我也在本地进行了测试。在 800MHz/512M PC 上,对 200,000 个英文单词的“精确短语”搜索不到 0.2 秒;在同一台 PC 上,对 700,000 个英文单词的“精确短语”搜索不到 0.5 秒(平均而言,一个英文单词由 4.5 个字符组成)。

源代码中的命名约定(枯燥的细节)

命名约定非常重要。它有助于创建自解释的源代码,便于项目评审和程序维护。

FTC 项目使用 C#(.NET Framework 1.1/VS.NET 2003)和 MSDE2000 完成。以下是在 FTC 项目中使用的命名约定

  • DataTable 的名称以 tdat 开头,例如:tdatArchiveEntry
  • 引用表(reference table)的名称以 tref 开头,例如:trefIndexStatus
  • 存储过程的名称格式为 up_SubjectVerb,例如:up_EntryAddStringChunk
  • 类成员变量的名称以小写字母开头,例如:sCurrentPath
  • 函数的名称以大写字母开头,例如:RefreshIndexProgress()Dal_sEntryText(int _ixArchiveEntry)
  • 函数局部变量的名称以_下划线开头,例如:_daWordSearch

关于变量的一些额外规则

  • 大多数变量的名称遵循模式:类型 + 主题 + [动词](大小写是其中的一部分)。
  • FTC 项目中不使用布尔变量。使用类似 _ynDoIndex 的变量来表示。 "yes" 表示执行索引,"no" 表示不执行索引。
  • "ix" 代表 "index",它是数组/列表/表/循环中的位置或主键。
  • "n" 代表 "status" 或 "state",它是 "n" 个选项中的一个状态。
  • "cnt" 是 "count"。该变量将计算数组/列表/表中的元素数量。
  • "s" 是 UNICODE 字符串;"as" 是 ASCII 字符串。
  • "conn" 是 "connection";"cmd" 是 "command"。

如何使用演示项目

userguide.chmprogrammerguide.chm 文件包含在演示项目文件夹中。

为了方便您测试演示,我已经在数据库中输入了 6 部莎士比亚戏剧。非常感谢 Project Gutenberg,他们提供了没有版权问题的免费文本。

最后

© 2005-2006 DEXLEAP.COM 版权所有。

本文的唯一授权版本可在以下网站获取: www.codeproject.comwww.dexleap.com

© . All rights reserved.