本文へジャンプ

Lotus > Lotus Developer Domain > 
   
 

DigestSearchメソッドによるLotus Dominoデータベースの検索

 
   
 
コンテンツ
パフォーマンス
DigestSearchはどのように機能するのか
DigestSearchをプロフィール文書と共に使用する
DigestSearchを単純な検索で使用する
Dominoディレクトリの検索のサンプル
DigestSearchの使用が適するケースと適さないケース
DigestSearchの次のバージョンに向けて必要なこと
速度が重要
ダウンロード
リソース
筆者について(原文のまま)

Andrei Kouvchinnikov, Principal Domino Developer, Botstation

レベル:初級
原文の掲載:2006年01月31日
更新日:2006年10月06日更新
原文はこちら (US)


この記事では、IBM Lotus Notesプロフィール文書を使用した作業と単純で高速な検索の代替ソリューションとして機能するDigestSearchについて紹介します。Lotus Notes Clientからサーバー・ベースのデータベースを検索するとき、DigestSearchは利用可能な他のどの検索方法よりも約2倍高速で、全文検索やLotusScriptのGetDocumentByKeyメソッドよりも優れたパフォーマンスを示します。

DigestSearchメソッドを一言で表現すれば、プロフィール文書を用いた検索での、LotusScriptのView.GetDocumentByKeyメソッドにかわる方法といえます。DigestSearchの主な目的は、1つの検索語を使用して1つ以上の文書を見つけることです。たとえば、社会保障番号、電話番号、Structured Query Language(SQL)レコードに格納されている固有のシーケンス番号などを使用した検索です。

DigestSearchの最大の利点は、大きなデータベースの検索で(特に、Lotus Notes Clientからサーバー・ベースのデータベースを検索する場合)、従来からのどの方法よりも、検索スピードが優れていることです。実際に、検索の複雑さにもよりますが、DigestSearchによる方法では最大で20倍も速くなります。また、DigestSearchは検索を実行するためにビューをまったく必要としないため、不要なビューを削除することによってデータベースのサイズを削減できます。DigestSearchによる検索では、データベースの文書数が実質的に検索速度に影響することはありません。

DigestSearchの短所は、ワイルドカードでないキーワードを1つしか受け取れないことです。この点で、この方法はLotusScriptのView.GetDocumentByKeyメソッドと@DBLookupに似ています。このため、パフォーマンスが重要で検索キーワードが予測可能な場合に、DigestSearchメソッドの使用を検討するとよいでしょう。DigestSearchをデータベースに実装する際は、大きな設計の変更や既存の文書への変更は不要です。この方法は、まだ改良の余地がありますが(特に、索引作成と複数キーワードでの検索に関し)、現在でも、非常に優れたパフォーマンスを提供します。

この記事で使用するサンプル・データベースのDigestprofile.nsf(プロフィール文書データベース)、Testindex.nsf(デモ用の索引データベース)、Digest2.nsf(簡単な検索用のDigestSearch)、およびDemonab.nsf(デモ用のDominoディレクトリ)は、Digest_dbs.zipファイルに収録されています。このファイルは、「ダウンロード」セクションからダウンロードして利用できます。

この記事では、DigestSearchメソッドを使用できる2つの方法を紹介します。

  • プロフィール文書および他の種類の一時保存文書の効率の良い代用として使用する
  • Dominoディレクトリを検索し、特定のグループに含まれるすべてのユーザーの個人情報を返す方法として使用する

この記事は、Lotus Notes/Dominoのプログラミングの経験がある方を対象として書かれています。

パフォーマンス

DigestSearchによる方法とLotus Dominoの従来の検索方法の検索速度を比較すると、次の2つの表に示すように、単一キーワードを使用した単一文書の検索では、DigestSearchの速度が他のすべての方法を上回っています。特に、データベースがサーバー上にあり、Lotus Notes Clientから検索を実行した場合に、良いパフォーマンスを得られます。このパフォーマンス・テストでは、100文書へのオブジェクト・ハンドルを取得するのに必要な時間を測定しました。測定は、Dominoディレクトリの検索サンプルで複数のステップで構成される検索をシミュレートした結果に対して行いました。[Digest Search 2]データベース(Digest2.nsf)に含まれる[Performance test]エージェントを使用することにより、このテストをご自分で実行できます。

メモ:このパフォーマンス・テストは、検索方法間での汎用の速度比較のためには設計されていません。結果は、グループのメンバーを検索する特定のタスクにのみ適用できます。

最初の表は、ユーザーがLotus Notes Clientからサーバー上のデータベースに検索を実行するようすをシミュレートしたものです。

検索方法時間(秒)
DigestSearch2.9
Db.Search13.1
Db.FTSearch6.1
View.GetDocumentByKey5.8
@DBLookup12.1

2番目の表は、Lotus Notes Clientからローカル・データベースに検索を実行したときの結果を示します。

検索方法時間(秒)
DigestSearch1.2
Db.Search9.7
Db.FTSearch2.8
View.GetDocumentByKey0.9
@DBLookup1.2

表からわかるように、@DBLookupをローカルで実行すると、2番目に速い方法になりますが、サーバー・ベースのデータベースに実行すると、2番目に遅い方法となります。(純粋なテキスト結果の代わりに文書ハンドルを返したため、結果に影響した可能性があります。)また、他の方法はサーバー上では少なくとも2倍遅くなっているのに対し、Db.Searchは30%しか遅くなっていない点も興味深い事実です。

メモ:このテストでは、Cacheパラメーターをオンにして@DBLookupを使用し、複数のキーワードを同じ検索に組み込みました。実際の検索では、常にこの方法が可能であるとは限りません。

上に戻る

DigestSearchはどのように機能するのか


DigestSearchメソッドはLotusScriptに完全に実装できます。この手法のコア・コードは約30行です。DigestSearchはバックグラウンド検索で使用でき、その構文と機能はView.GetAllDocumentsByKey("searchword",True)メソッドに似ています。この2つの方法で最も似ているのは、どちらも1つのキーワードをパラメーターとして受け取り、一致する1つ以上の文書を結果として返す点です。違いとしては、DigestSearchは検索の実行にビューを必要とせず、検索語に完全に一致するものを常に検索します。

DigestSearchは、次のLotusScriptコマンドを使用して呼び出します。
Set doc=FastSearchByKey(db, "searchword")

この方法は、固有のダイジェスト(不可逆ハッシュ)値を使用して検索語を表すので、DigestSearchと呼ばれています。固有キーは、暗号化された検索語で、32文字のストリングになります。これは、文書のUniversal ID(UNID)として使用されます。つまり、DigestSearchは実際にはデータベースを検索せず、特定のUNIDを持つ文書が存在するかどうかを単にチェックしています。

検索フローの簡単な例を示します。検索フローがどのように機能するかを理解することで、変更やカスタマイズが容易になります。

  1. ユーザーは、電話番号の所有者の名前を見つけるために、電話番号「+1 212 12345678」を検索します。
  2. @Password関数によって、「+1 212 12345678」はダイジェスト値に変換されます。得られたダイジェスト/ハッシュ・ストリングは「3F915F67F52D35053113AAB40385FE46」です。
  3. スクリプトは、「3F915F67F52D35053113AAB40385FE46」というUNIDを持つ文書がデータベース内に存在するかどうかをチェックします。
  4. 文書が見つかると、検索は終了します。スクリプトは、文書からフィールドを読み取ってユーザーに表示するか、ユーザー・インターフェースで単に文書を開きます。文書が見つからない場合は、そのようなキーワードを持つ文書がないことを示すメッセージがユーザーに表示されます。

次のコードは、ダイジェスト検索を実行する簡単な@式の例です。

seachrword:="+1 212 12345678";
fullname:=@GetDocField(@Middle(@Password(seachword);1;32);
"FullName");@Prompt([Ok];"Result for "+searchword;
@If(@IsError(fullname);"Document "+searchword+” 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+|")|)

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)

LotusScriptライブラリーのDigestSearchLibの全体のコードは次のとおりです。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をプロフィール文書と共に使用する


Lotus Notesはプロフィール文書をキャッシュに保管します。この動作は、複数のユーザーが同時にプロフィール文書を変更すると、問題を引き起こす可能性があります。キャッシュの問題により、ユーザーは古い、期限切れとなった値を取得します。しかし、標準のGetProfileDocument機能の代わりにDigestSearchを使用することにより、この問題を解決できます。コードの一部を以下に示します。

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

次のコードのように、式言語を使用することによっても、新しいダイジェスト・プロフィール文書にアクセスできます。

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))

残念ながら、Lotus Notesの@式言語を使用して新しいダイジェスト・プロフィール文書を作成することはできません。既存文書の検索と変更だけが可能です(@SetDocFieldを使用します)。この記事の「ダウンロード」セクションには、[DigestSearch demo for profile docs]データベース(Digestprofile.nsf)を含むZIPファイルがあります。このデータベースには、[Modify example with Formula]エージェントのソース・コードとサンプルが含まれています。また、このデータベースには、テスト用のLotusScriptと@式の両方のコードが含まれています(図1参照)。[Instructions]ビューで[Create profile doc]をクリックすると、新しいプロフィールが作成され、[Find profile doc]をクリックすると、作成したプロフィールを検索できます。

図1.プロフィールのテスト用のアクション・ボタン

上に戻る

DigestSearchを単純な検索で使用する


検索で使用する方が、プロフィール文書と共に使用するよりも複雑になります。プロフィール文書の数が制限されることがあります。プロフィール文書はオンデマンドで作成され、他の文書に依存しません。相互にリンクされていたり、他のデータベースとリンクしている数十万もの文書が含まれるデータベースの検索では、このようなケースはありません。

既存のデータベースの整合性を維持するために、これらのデータベース内で文書のUNIDを直接変更することはできません。このため、親データベース内の文書に対応する索引文書を維持する特殊なミラー/索引用のデータベースが必要です。

索引データベースには設計要素が不要で、単に次の2種類の文書が含まれています。

  • 参照ホルダー文書(Reference holder documents)
  • キーワード・ホルダー文書 (Keyword holder documents)

ソース・データベース内の各索引文書ごとに、1つの参照ホルダー文書(SourceRefHolderフォーム)が存在します。この参照ホルダー文書には、動的に作成される2つのフィールドSKeyとREFUNIDsがあります。SKeyフィールドは、検索用でなく、ソース・データベース内のオリジナル文書のUNIDの確認用としてのみ使用します。REFUNIDsフィールドは複数値フィールドで、キーワード・ホルダー文書をトラッキングするために使用します。つまり、このフィールドには、キーワード・ホルダー文書のUNIDが格納されます。

ソース文書あたりのキーワード・ホルダー文書の数は、各文書にある検索フィールドの数と同じです。これらのフィールドは、設定文書で指定します(図2参照)。それぞれの索引文書には、複数値フィールドのUNIDsがあり、このフィールドには、その索引文書に割り当てられたキーワードに一致するソース・データベース内のすべての文書のUNIDが格納されます。

図2.設定文書

検索のソース・コードは、前に説明したプロフィール文書の例と似ています。違いは次のとおりです。

  • 検索では、現在のデータベースの代わりに[Demo Index]データベース(Testindex.nsf)を使用します。
  • 複数のキーワードを1つの特殊文書で維持します。

[Demo Index]データベースが更新されていて、このデータベースにソース・データベース(Dominoディレクトリ)からのすべての文書が含まれているものとすると、検索の結果は、ソース・データベースを検索したときと同じになります。

新しい文書で[Demo Index]データベースを更新するには、次の3つの方法があります。

  • ソース・データベースでQuerySaveスクリプトを文書に実行する
  • スケジュール済みエージェントを使用する
  • 文書の変更直後に更新を透過的に実行するアドオン・サーバー・プログラムを使用する

メモ:[Digest Search2]データベース(Digest2.nsf)には、ソース・データベースの文書の索引を作成する[Process database index]エージェントが含まれています。

上に戻る

Dominoディレクトリの検索のサンプル


サンプルの[Digest Search2]データベースには、Dominoディレクトリの検索を実行する2つのエージェントが含まれています。一方のエージェント(Find users by first name)は、ユーザーの名(ファーストネーム)が、入力ボックスに入力した名前と一致するすべてのDominoディレクトリ文書を検索します。もう一方のエージェント(Find group members)は、指定されたグループに属するすべてのユーザー文書を検索します。サンプル・エージェントを実行するには、図3に示す3つのデータベースが必要です。

  • Digest Search2(Digest2.nsf)
  • Demo NABデータベース(Demonab.nsf)。Dominoディレクトリのコピーを使用することもできます。
  • Demo Index(Testindex.nsf)。索引を保存する空のデータベースです。

図3.サンプル・エージェント用のデータベース

これらの3つのデータベースは、「ダウンロード」セクションにあるZIPファイルに含まれています。

図2と図4に示すように、[Digest Search2]データベースで、[Demo NAB]データベースと[Demo Index]データベースの場所を設定する必要があります。データベースのパスを設定した後は、[Demo NAB]データベースに含まれる情報を使用して、索引データベースを生成しなければなりません。これを行うには、[Configuration]ビューで[Synchronize Index]をクリックします(図4参照)。

図4.[Configuration]ビューのアクション・ボタン

索引の作成が完了したら、検索を実行できます。まず、[Find persons by first name]または[Find all users in a group]をクリックします。次に、ユーザーのファーストネームまたはグループ名を入力し、[OK]をクリックします。この例では、ユーザーのファーストネームは「John」です。この名前は、ご使用になるディレクトリーに存在する適当な名前に変更できます(図5参照)。

図5.ファーストネームによる検索

すべてが適切に設定されている場合は、図6のようなウィンドウが表示されます。

図6.ファーストネームによる検索の結果

[Find all users in a group]をクリックすると、次のバックグラウンド・イベントが発生します。


  1. スクリプトは、「keyword listname=demogroup」に一致する索引文書(キーワード・ホルダー文書)を識別します。
  2. 索引文書で、スクリプトはソース・データベースのソース文書のUNIDを識別します。
  3. スクリプトは、ソース文書からMembersフィールドを読み取ります。次に、このフィールド内の各ユーザーに対し、新たに作成されたキーワード「"fullname="+doc.members(x)」を使用して、新しい検索を実行します。
  4. フィールド内のすべてのユーザーが処理されるまで検索はループし、結果を1つのテキスト・ストリングにまとめます。
  5. MessageBoxによって、結果として得られたテキスト・ストリングをユーザーに表示します。
上に戻る

DigestSearchの使用が適するケースと適さないケース

DigestSearchは、単一のキーワードを使用して1つ以上の文書を素早く検索するときに使用します。プロフィール文書やデータの一時保存用の文書は、DigestSearchが最も適している領域です。同様に、何らかの理由で従来の検索方法を使用できないにもかかわらず、高いパフォーマンスの検索ソリューションが必要な場合にもDigestSearchを使用します(Dominoディレクトリのような静的なデータが適しています)。

DigestSearchを使用することを検討した方がよい実例を示します。

  • 社会保障番号または電話番号からユーザー文書を検索するとき
  • 品番から製品を検索するとき
  • SQLによる同期で、固有のレコード番号を使用して、そのレコードがLotus Notesデータベースにすでに存在しているかどうかを判断するとき
  • 特定のグループのすべてのメンバーについて、Dominoディレクトリのユーザー文書から特定のフィールドを取得するとき

DigestSearchを使用しない方がよい実例は、次のとおりです。

  • 標準の検索方法で満足できる速度が得られるとき(1秒未満)
  • データベースが大きくないとき(5,000文書未満)
  • 複数のキーワードを定義することが必要なとき(例: Form="Document" & Status="Active")
  • より柔軟性の高いキーワードを使用することが必要なとき(例: @Left(Product;2)="Di",@Created=@Today)
  • 特定のキーワードによる検索結果として返される文書の数が 500を超えるとき

もともと、DigestSearchメソッドは、大量のSQLレコードをLotus Dominoデータベースと同期するときの速度を改善するために作成されました。各SQLレコードには固有のUnique Sequence Numberがあり、レコード内のすべての結合済みフィールドのカスタム固有番号を計算することができます。各レコードは1回だけ存在します。これは、DigestSearchメソッドを適用するために最も適した状況です。既存の文書(私たちのケースでは、1,000,000を超える文書)を配列またはリストにキャッシュすることを避けられるため、同期の時間を25分から10分に削減できます。

上に戻る

DigestSearchの次のバージョンに向けて必要なこと

前述のように、DigestSearchには改良の余地があります。解決が簡単なものも、新しいアプローチが必要なものもあります。DigestSearchメソッドの次のバージョンで、私たちが計画している変更点は次のとおりです。

  • 現在、新しい文書で索引データベースを更新するエージェントはやや遅く、最適化されていません。いくつかのトラッキング・フィールドを索引文書に追加することで、解決できます。
  • 増分の索引作成の実装が可能かどうかチェックします。
  • 新規文書または変更された文書をソース・データベースから索引データベースへ自動的に追加するサーバー・アドオンを開発します。
  • 各ソース文書ごとに1つの索引文書を作成する代わりに、同じ索引文書を使用して、4,096個のソース文書への参照を保存します。(この変更がパフォーマンスに影響するかどうか詳細に調査することが必要です。)
  • 1回の検索で複数のキーワードを許可します(たとえば、「ラストネーム」と「市」など)。この機能は、複数の個別の検索結果を比較し、一致しない結果を廃棄することで実現できます。
  • 複数のソース・データベースが同じ索引文書を使用できるようにします。
  • パフォーマンスと索引の管理性を改善するために、B-Tree検索手法を実装することの可能性を考慮します。(現在、カスタマイズしたB-Treeアルゴリズムを使用して、ワイルドカードを用いた検索や、索引文書のよりコンパクトな保存が可能かどうかを調査しています。特定のキーワードを迅速に検索できるDigestSearchの特性と相まって、画期的なソリューションになるでしょう。)
上に戻る

速度が重要

標準的な状況では、DigestSearchは通常の検索方法よりも素早く結果を得られます。FTSearchやDBSearchによる高度な検索ほどの機能は必要としないが、プロフィール文書、GetDocumentByKeyメソッド、@DBLookupの代わりとなる方法を探している場合は、サンプル・データベースをダウンロードし、DigestSearchが役に立つかどうかを確認してください。この記事に掲載されている例とサンプル・データベースを用いることにより、DigestSearchの機能を簡単にテストできます。また、設定文書を変更するだけで、DigestSearchをご自分のアプリケーションに実装できます。

上に戻る

ダウンロード

説明 ファイル名 サイズ ダウンロード方法
この記事のサンプルアプリケーション digest_dbs.zip 1284KB HTTP
ダウンロード方法について(US)
Adobe® Reader®が必要

上に戻る

リソース

学ぶ
製品とテクノロジーを手に入れる
議論する
上に戻る

筆者について(原文のまま)

Andrei Kouvchinnikov is a certified Principal Domino Developer and Administrator. His experience includes full life cycle development of Lotus Domino applications running on multiple platforms and development of applications for QuickPlace and Sametime. He has been working with the Lotus Domino platform since R4.5 for OS2. You can reach Andrei at andrei@botstation.com.

上に戻る