扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
对 DigestSearch 方法的最佳描述是,它是用于处理 Profile 文档的 LotusScript 的 View.GetDocumentByKey 搜索方法的替代解决方案。它的主要目的是使用一个关键字查找一个或多个文档,例如使用社会保障号(Social Security Number)、电话号码或者是一个存储的结构化查询语言(Structured Query Language ,SQL)记录的惟一序号(Unique Sequence Number)。
DigestSearch 的主要优势是搜索大数据库(尤其是搜索来自 Notes 客户机的基于服务器的数据库),就速度而言它的性能要优于所有其他常规方法。实际上,依据搜索复杂程度的不同,DigestSearch 方法最多可以快 20 倍!并且 DigestSearch 执行搜索不需要任何视图,所以可以通过移除不必要的视图来使数据库变小。数据库中文档数目的多少并不会对使用 DigestSearch 方法进行搜索的速度产生实质性的影响。
DigestSearch 最大的缺点是它仅能够接收一个不含通配符的关键字。就这一点而言,该方法与 LotusScript 的 View.GetDocumentByKey 方法和 @DBLookup 类似。所以,如果性能对您来说很重要,并且关键字是可以预知的,就可以考虑使用 DigestSearch 方法。在数据库中实现 DigestSearch 方法并不需要重大的设计更改或者是对现有文档的修改。尽管该方法还有一些需要改进的地方(尤其是在进行多个关键字索引和搜索方面),但是它现在已具有极大的性能优势。
本文中讨论的样例数据库 Digestprofile.nsf(Profile 文档数据库)、Testindex.nsf(Demo Index 数据库)、Digest2.nsf(DigestSearch 简单搜索)和 Demonab.nsf(demo Domino 目录)都包含在 Digest_dbs.zip 文件中,可从 “下载” 一节下载。
在本文中,展示了两种使用 DigestSearch 方法的方式:
本文假设您是经验丰富的 Notes/Domino 程序员。
性能
当您比较 DigestSearch 方法和传统 Domino 搜索方法的搜索速度时,如下面的两个表所示,在使用单一关键字搜索单一文档时,DigestSearch 的性能好于所有其他方法,尤其是当数据库驻留在服务器而您在 Notes 客户机上执行搜索时。在我们的性能测试中,我们测量了获取一个对象来处理 100 个文档所需的时间。我们测量了 Domino Directory 搜索示例的结果,并且模拟了一个由几个步骤组成的搜索。您可以使用 Digest Search 2(Digest2.nsf)数据库中的 Performance 测试代理来运行自己的测试。
注意:该性能测试并不是设计用来进行方法之间的一般速度对比;结果仅适用于搜索组中成员的特定任务。
在第一个表中,模拟用户在 Notes 客户机上执行一个对服务器上数据库的搜索:
搜索方法 | 时间(秒) |
DigestSearch | 2.9 |
Db.Search | 13.1 |
Db.FTSearch | 6.1 |
View.GetDocumentByKey | 5.8 |
@DBLookup | 12.1 |
第二个表显示了在 Notes 客户机上对本地数据库进行搜索所得到的结果:
搜索方法 | 时间(秒) |
DigestSearch | 1.2 |
Db.Search | 9.7 |
Db.FTSearch | 2.8 |
View.GetDocumentByKey | 0.9 |
@DBLookup | 1.2 |
正如我们所看到的,在本地运行时 @DBLookup 是第二快的方法;当在基于服务器的数据库上运行时,它是第二慢的方法。(然而,返回文档句柄而不是返回纯文本结果也会影响测试结果。)另一个有趣的事实是,搜索本地数据库时 Db.Searchis 方法所用的时间只比搜索服务器所用的时间少 30%,而其他方法都至少减少 50%。
注意:在该测试中,我们在同一查询中使用了带有 Cache 参数和几个关键字组合的 @DBLookup,在实际搜索中这样做并不总是可行的。
DigestSearch 是如何工作的
DigestSearch 方法完全是用 LotusScript 实现的,并且它的核心代码大约只有 30 行。可以使用 DigestSearch 进行后台搜索,并且它的语法和功能会让人想起 View.GetAllDocumentsByKey(实际上是 “searchword”)方法。这两个方法主要的共同之处是,它们都使用一个关键字作为参数,并返回一个或多个精确匹配的文档作为结果。不同之处是执行 DigestSearch 搜索不需要视图,并且它总是进行搜索字的精确匹配。
使用 LotusScript 命令调用 DigestSearch:
Set doc=FastSearchByKey(db, "searchword")
该方法称作 DigestSearch 是因为它使用惟一摘要(单向散列)值来表示一个搜索字。该惟一键是一个加密的搜索字,结果是一个 32 个字符的字符串,这个字符串然后被用作文档的 Universal ID (UNID)。因此,DigestSearch 并不是真正搜索数据库;它只是简单地检查文档是否存在指定的 UNID。
这是搜索流程的一个简单例子。当理解了该流程是如何工作的以后,就可以很容易地进行修改和定制:
下面的代码显示了一个简化了的可以工作的执行摘要搜索的 @formula 示例:
seachrword:="+1 212 12345678"; fullname:=@GetDocField(@Middle(@Password(seachword);1;32); "FullName");@Prompt([Ok];"Result for "+searchword; @If(@IsError(fullname);"Document "+searchword+a?? does not exist"; "Fullname: "+fullname)) |
上面代码中使用的 LotusScript 代理代码是:
Option Public Use "DigestSearchLib" Sub Initialize Dim session as New NotesSession Dim db As NotesDatabase Dim doc As NotesDocument, curdoc as NotesDocument Dim workspace As New NotesUIWorkspace Dim uidoc As NotesUIDocument Dim searchstring As String Set uidoc = workspace.CurrentDocument Set curdoc=uidoc.CurrentDocument Set db=session.CurrentDatabase searchstring=doc.inputfield(0) ' Phone number input field on form ' Exit if no phone number was supplied If searchstring = "" Then Exit Sub Set doc= FastSearchByKey(db, searchstring) ' Perform DigestSeach and ' return document handle If Not doc Is Nothing Then curdoc.FullName=doc.FirstName(0)+" "+doc.LastName(0) ' populate fields in current document with information ' from the found document curdoc.Address=doc.Address(0) curdoc.Job=doc.JobDescription(0) Else ' No document was found, notify user about it Msgbox "Info for "+searchtxt+" not found" End If End Sub |
在后台的脚本库中,当运行该代理时将会执行下列过程:使用 @Password 方法将搜索字转换为摘要。(这一步骤得到一个与 UNID 兼容的 32 个字符的字符串。)
ev=Evaluate(|@Password("|+skey+|")|)
该库检查是否存在一个带有这个 UNID 的文档:
On Error 4091 Goto wrongiderr4091 Set digestdoc= digestdb.GetDocumentByUNID(Mid(ev(0),2,32))
注意:必须处理可能出现的 Invalid universal ID 错误,在搜索摘要没有相应文档时,会出现这个错误。
如果 digestdoc 没有导致错误 4091 Invalid universal ID,脚本会将文档的句柄传递回调用的函数:
Set FindDocByDigestKey=digestdoc Exit Function wrongiderr4091: Set FindDocByDigestKey=Nothing |
脚本为用户显示找到的文档中的值:
curdoc.FullName=doc.FirstName(0)+" "+doc.LastName(0)
下面的代码显示了整个 DigestSearchLib LotusScript 库,其中包含了 DigestSearch 的核心代码:
' ---- DigestSearchLib script library start ------ Dim digestdb As NotesDatabase Dim digestdoc As NotesDocument Dim lastkey As String Dim lastgeneratedID As String Dim lastgeneratedDoc As NotesDocument Function FindDocByDigestKey(custdb As NotesDatabase,skey As String)_ As NotesDocument ' this is main function for getting search result Dim unid As String Set digestdb=custdb On Error Goto errh unid=CalculateDigest(skey) If Len(unid)<>32 Then Set FindDocByDigestKey=Nothing Exit Function End If Set digestdoc = lastgeneratedDoc ' lastgeneratedDoc is a global variable 'return document handle back to calling agent Set FindDocByDigestKey=digestdoc Set digestdoc=Nothing Exit Function errh: Set FindDocByDigestKey=Nothing 'no document for that keyword is found Resume endas endas: End Function Function IsDigestKeyTaken(unid As String) ' this function checks if document for the keyword already exist On Error Resume Next ' error nr 4091 means invalid Universal ID On Error 4091 Goto wrongiderr4091 ' check if document with unique keyword already exist Set digestdoc= digestdb.GetDocumentByUNID(unid) IsDigestKeyTaken=True Set lastgeneratedDoc=digestdoc Exit Function wrongiderr4091: IsDigestKeyTaken=False Resume wrongID wrongID: End Function function CalculateDigest(skey As String) 'this function computes 32-character digest for the keyword Dim unid As String 'calculate digest for the keyword ev=Evaluate(|@Password("|+skey+|")|) unid=Mid(ev(0),2,32) 'strip parentheses around generated digest lastgeneratedID=unid 'assign global variable a new value If IsDigestKeyTaken(unid)=False Then unid="no doc with that digest yet" End If CalculateDigest=unid End Function Sub MakeSearchable(sdoc As NotesDocument) ' this function sets new Universal ID and saves the document unid=lastgeneratedID sdoc.UniversalID=unid End Sub ' ----- script library end ------ |
使用 DigestSearch 处理 Profile 文档
您可能已经注意到了,Lotus Notes 对 Profile 文档进行缓存。如果两个或更多的用户同时修改 Profile 文档,那么这一操作就会出现问题。由于缓存的问题,用户会得到旧的、过期的值。但是可以使用 DigestSearch 替代标准的 GetProfileDocument 函数来解决该问题,如下面的代码片段所示:
Set db=session.CurrentDatabase ' Perform search and return a document handle Set profiledoc = FastSearchByKey(db, "My Profile 1") If Not profiledoc Is Nothing Then MsgBox profiledoc.Field1(0) ' show a field from profile document profiledoc.Field2=Cstr(Now) ' update profile with new field Call profiledoc.save(True,False) 'save profile document End If |
甚至还可以使用 formula 语言来访问新的摘要 Profile 文档,如下面的代码所示:
profname:="My Profile 1"; comment:=@GetDocField(@Middle(@Password(profname);1;32); "comment"); createddate:= @GetDocField(@Middle(@Password(profname);1;32); "doccreated"); @Prompt([Ok];"Result for "+profname; @If(@IsError(comment); "Profile "+profname+"does not exist"; "Comment: "+comment+@Char(10)+"Created: "+createddate)) |
不幸的是,不能使用 Notes @formula 语言创建新的摘要 Profile 文档。只能够搜索和修改现有的文档(使用 @SetDocField)。本文的 “下载” 一节包含一个 ZIP 文件,其中有一个名为 “DigestSearch demo for profile docs”(Digestprofile.nsf)的数据库,包含了 “Modify example with Formula” 代理的源代码和示例。该数据库还包含了 LotusScript 和 @formula 代码,可以使用它们进行测试(参见图 1)。在 Instructions 视图中单击 Create profile doc 创建一个新的 profile 文档,然后单击 Find profile doc 找到刚刚创建的 profile 文档。
图 1. 用于 profile 文档测试的 Action 按钮
使用 DigestSearch 进行简单搜索
进行搜索比处理 Profile 文档更加复杂。Profile 文档的数量通常是有限的;它们是根据需要创建的,并且不依赖于其他文档。这种情况与搜索包含无数互相连接并与其他数据库相连接的文档的数据库时完全不同。
为了维护现有数据库的一致性,不能直接修改这些数据库中文档的 UNID。因此,需要一个特殊的镜像/索引数据库用于保存父数据库中文档的索引文档。
索引数据库不需要任何设计元素;它只包含两种类型的文档:
源数据库中对每一个进行了索引的文档存在一个引用持有者文档(即一个 SourceRefHolder 表单)。该引用持有者文档包含两个动态创建的字段:SKey 和 REFUNIDs。SKey 字段只是用来提示源数据库中最初文档的 UNID,而不用于搜索。REFUNIDs 字段是一个多值字段,用来对关键字持有者文档进行跟踪。也就是说该字段包含关键字持有者文档文档的 UNID。
每一个源文档的关键字持有者文档的数目与每个文档的搜索字段的数目相同。在 Configuration 文档中指定这些字段,如图 2 所示。每一个索引文档也具有多值字段 UNID,其中包含与为该索引文档分配的关键字相匹配的源数据库中的所有文档的 UNID。
图 2. Configuration 文档
搜索的源代码与上文中提到的 Profile 文档示例类似。不同之处是:
假设更新了 Demo Index 数据库,并包含来自源数据库(也就是 Domino Directory)的所有文档,搜索得到的结果与搜索源数据库得到的结果相同。
可以通过以下三种主要方式,使用新文档更新 Demo Index 数据库:
注意:Digest Search 2 数据库(Digest2.nsf)包含一个称作 Process database index 的代理,用于为源数据库中的文档创建索引。
搜索 Domino Directory 示例
在样例数据库 Digest Search 2 中包含两个用于执行 Domino Directory 搜索的代理。其中一个代理(Find users by first name)查找所有这样的 Domino Directory 文档,即其中人的名字与您在输入框中输入的名字匹配。另一代理(Find group members)查找指定组中的人的所有 Person 文档。要运行示例代理需要三个数据库,如图 3 所示:
图 3. 示例代理的数据库
三个数据库全部都包含在本文 “下载” 一节的 ZIP 文件中。
在 Digest Search 2 数据库中,必须配置 Demo NAB 和 Demo Index 数据库的位置,如图 2 和图 4 所示。为数据库配置了路径后,必须使用来自 Demo NAB 的信息填充索引数据库。要完成该操作,在 Configuration 视图中单击 Synchronize Index(参见图 4)。
图 4. Configuration 视图中的 Action 按钮
一旦完成索引后,就可以执行搜索。只需单击 “Find persons by first name” 或者 “Find all users in a group”。然后输入用户的名字或组名,并单击 OK。在我们的例子中,用户的名字是 John。您应该将其更改为存在于您目录中的适当名字(参见图 5)。
图 5. 按名字进行搜索
如果一切都进行了正确的配置,将显示类似图 6 所示的窗口。
图 6. 按名字进行搜索的结果
当您单击 “Find all users in a group” 时,将发生下列后台事件:
何时(及何时不)使用 DigestSearch
当需要用一个关键字快速查找一个或多个文档时,可以使用 DigestSearch。Profile 文档和用于临时存储数据的文档是最明显的使用场所。类似地,不管什么原因,在您无法使用传统的搜索方法,而是需要一个高性能的解决方案时(更适合于静态数据,例如 Domino Directory 中的数据),可以使用 DigestSearch。
下面是应该考虑使用 DigestSearch 的实际示例:
何时不 使用 DigestSearch 的实际示例包括:
DigestSearch 方法最初创建的目的是为了提高大量 SQL 记录与 Domino 数据库进行同步的速度。每一条 SQL 记录都有它自己的惟一序号,您可以为记录中的所有组合字段计算一个定制的惟一编号。每一条记录都是惟一的,这就为使用 DigestSearch 方法提供了一个绝佳的环境。可以避免将现有的文档(在我们的例子中,有超过 1,000,000 个文档)缓存到数组或者列表中,因而同步所需的时间会缩短 10 到 25 分钟。
下一个 DigestSearch 版本的任务列表
正如上文中提到的,DigestSearch 还有一些改进的空间。一些问题可以很容易地解决,而其他一些问题还需要新的方法。在 DigestSearch 方法的未来版本中,可能将会看到下面一些更改:
速度非常重要
在通常情况下,DigestSearch 能够比常规的搜索方法更快地得到结果。如果您不需要 FTSearch 和 DBSearch 所提供的奢侈的高级查询,而是在寻找 Profile 文档、GetDocumentByKey 方法或 @DBLookup 的替代方法,那么下载样例数据库并看一看 DigestSearch 是否对您有用。在本文中的示例和样例数据库的帮助下,您可以花几分钟的时间来测试一下 DigestSearch 的功能,并且只需修改 Configuration 文档就可以在您自己的应用程序中加以实施。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者