|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
IBM Lotus Notes/Dominoでのより速い検索のためのコーディング |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Raphael Savir, Principal Developer, LS Development Corporation レベル:中級原文の掲載:2007年 3月 13日
この記事では、おそらくIBM Lotus Notes/Dominoで最も使用されている@関数である@DbLookupを取り上げます。IBM Lotus Notes/Dominoアプリケーションの現在の開発者は、この式を使わずにアプリケーションを作成することなど想像できないでしょう。15年を超えるパフォーマンス・テストおよびお客様のトラブルシューティングによると、この式はアプリケーションの複数のフォームで一般的に使用されていて、1つのフォームで数十回も使用されることがしばしばありました。 しかし、同時に、これらの@DbLookup式にはパフォーマンスの問題も頻繁に発生することが示されています。私たちは、洗練されたエンタープライズ・アプリケーションが、これらの式に関連するあまりにも遅いパフォーマンスによって仮想的に停止してしまう場面を見てきました。 この記事では、ほとんどすべてのアプリケーションのスピードアップを保証する11のヒントについて説明します。これらのヒントは、1行だけの変更から、@DbLookup式へのアプローチ方法を劇的に変えてしまうケースまで広い範囲にわたりますが、いずれも年月をかけて有効性が証明されているものです。 IBM Lotus Notes/Domino開発の知識をある程度持っていることを前提として説明を進めるため、@DbLookup式の基本的な引数などについては触れません。 この記事では、さまざまな例を使用してヒントの有効性を示します。説明を簡単にするために、アプリケーションを1つだけ使用します。連絡先(Contact)フォームとチケット(Ticket)フォームを持つヘルプ・デスク(Help Desk)アプリケーションです。このアプリケーションの基本的なワークフローは次のようになります。まず、お客様から連絡があると、チケット文書を作成します。次に、お客様の会社および名前に関する情報を取得し、ドロップダウン・リストから適切な会社名と連絡先名を選択します。また、連絡の適切なカテゴリー(たとえば、製品ヘルプ、営業など)を選択します。このアプリケーションでは、@DbLookup式が役に立つ次のようなケースがすぐに思い浮かびます。
エラーをトラップするエラーがないことを願っていても、エラーのトラップは必要となります。@DbLookup式はキーおよび返されるデータ用のユーザー入力に依存することが多いので、@DbLookup式では扱いにくいエラーがしばしば発生します(この記事では、@DbLookupは@DbLookup式と@DbColumn式の両方を示します)。つまり、ある時点では式が正しく機能していても、翌日には正しく機能しないことがあります。開発者にとって、これは常に困難な状況です。 たとえば、ある会社の会社名が、異なる連絡先文書で3、4とおりのスペルで入力されているケースを考えます(「LSDevelopment Corporation」と「LS Development Corporation」など)。その会社の各連絡先の名前を検索するときに、「LSDevelopment Company」と入力すると、一致するものは見つからないでしょう。 より正確にキーを入力する方法はありますが(たとえば、ドロップダウン・リストを使用する)、説明のために、厳密な開発手法を使用しても @DbLookupはエラーを返すことがあると仮定しましょう。 エラーをチェックする基本的な方法は、リスト1に示すコードを記述することです。 リスト1. エラー・チェック
このコードでは、検索を実行し、その後でエラー条件をチェックします。エラーが存在する場合は、問題があることをユーザーに示すテキスト・ストリングを返します。それ以外の場合は、検索結果の値を返します。 これはパフォーマンスには関係しませんが、多くのアプリケーションに見られるあまりにも基本的な問題なので、無視することはできません。特別なヒントと考えてください。
検索の頻度を最小限にする検索式の次のコードを検討します。 @If(@IsError(@DbColumn(“Notes”; “”; “(Lookup-Companies)”; 1); “There
are no これは、検索を2回実行している非常に一般的なミスで、パフォーマンスの観点から問題となります。最初の検索で検索結果が完全にキャッシュされるので、2回目の検索はフリー(負担にならない)と考えているのでしょうが、これは正しくありません。結果は確かにキャッシュされるため、2回目の検索は最初の検索よりも速くなりますが、フリーではありません。そのような方法でコーディングする機能的な理由はないため、以下のようにコーディングする必要があります。 v := @DbColumn(“Notes”; “”; “(Lookup-Companies)”; 1); 不必要な検索が生じるもう1つの誤りとして、以下のような場合に計算を行う計算結果フィールドの検索式があります。
最も頻度が高いのは、文書を初めて作成するときに、検索を1回だけ実行するケースです。この場合は、フィールドを「作成時の計算結果」にして、それ以上の計算を防ぎます。 これとは別に、文書を編集モードで開くたびに、検索を実行するケースが考えられます。この場合は、式を以下のように書き換えます。 @If(@IsDocBeingEdited; “”; @Return(FieldName)); ここで、FieldNameはこの式が置かれているフィールドの名前です。文書が編集モードでロードされていない場合は、式はそのままの値を保持し、実行を停止します(図1参照)。それ以外の場合は、以前と同様に@DbColumnを実行します。 図1. キーワード式![]()
キャッシュを正しく使用するもう1つの一般的な誤りとして、NoCache引数の使い方を誤解していることが挙げられます。つまり、データが重要であるほどNoCacheを使用することの重要性が増すと考えるのは誤りです。実際には、検索を実行する頻度に対して、データの変更頻度はどうなのかを考慮して判断します。 たとえば、検索するデータがあまり変更されない場合は(月ごとの変更など)、検索式にNoCacheを使用することの必要性はまったく考えられません。この記事の例では、参照用に数個のカテゴリーをチケット文書に作成したものとします。リストはアプリケーションの所有者によって維持され、新製品がリリースされたとき、新規のビジネス・ベンチャーが参入したときなど、リストは数カ月ごとに更新されるぐらいです。 一方、連絡してきたお客様をチケット文書で検索するケースを考えます。このお客様が1時間に2回連絡してくる場合は、2つめのチケット文書で名前の検索をミスすることはあり得ないでしょう。名前のリストは、前回の検索でキャッシュされているからです。このような種類のアプリケーションでは、ヘルプ・デスクのスタッフが1日中データベースを開いたままにしていることをよく見かけるので、キャッシュに対応した検索を長時間キャッシュされたままにすることは容易です。
ドロップダウン・リストの落とし穴を避ける私たちは長年、深刻なパフォーマンスの問題を抱えるフォームを見てきました。その原因の中でも1番に挙げられるのがドロップダウン・リストで、理由は2つあります。ドロップダウン・リストは大きなリストを検索することがよくあること、そして読み込みモードでも計算を行うためです。 アプリケーションで簡単なテストを実行できます。遅いと感じられるフォームを使用する文書を読み込みモードで開きます。文書が開くときに、画面を注意深く見て、一時停止する場所を見つけてそれを書き留めます。画面が変わるまで一瞬なのでたいへんな作業かもしれませんが、他の人に手伝ってもらい、一方がフィールド・ラベルを読み上げ、もう一方が書き留めるとよいでしょう。次に、IBM Lotus Domino Designerでこのフォームを開き、これらの場所の直下に何があるのかを調べます。ほとんどの場合、それは@DbLookup式をともなうドロップダウン・リストでしょう。一体、何が起きているのでしょうか。 文書が読み込みモードで開かれるとき、すべての@DbLookup式が検証されます。文書が読み込みモードなので、これらの式は実際には値を返しませんが、動作を実行するので時間がかかります。ドロップダウン・リストではさらに悪いことに、ユーザーが編集モードに切り替えたときのために、選択可能な値がキーワード・フィールドに生成されます。ユーザーが編集モードに切り替えるまで待って実行すれば、時間のコストをユーザーに押しつけられる、と考えるかもしれませんが、Lotus Notesではそのように動作しません。興味深いことに、これは正にWebブラウザーでの動作です。まったく同じフォームに対しても、このように動作します。 ドロップダウン・リストのクリーンアップとして実績のある1つの方法を紹介します。3つの異なる機能を組み合わせて不要な検索を避ける方法です。それぞれは簡単な作業ですが、3つをすべて行う必要があります。 最初に、ドロップダウン・フィールドの式で、以下の式を使用します。 @If(@IsDocBeingEdited; “”; @Return(FieldName)); これは、「検索の頻度を最小限にする」で説明されているものと同様で、文書が読み込みモードの場合は、キーワード・ドロップダウン・リストが検索を実行するのを防止します。ユーザーが編集モードで文書を開いたときは、もちろんドロップダウン・リストは通常どおり計算を行います。ここで、ユーザーが読み込みモードから編集モードに切り替えたときに、検索が確実に計算されるようにしなければなりません。これを次の2つのステップで実行します。 ドロップダウン・フィールドのプロパティーで、「文書の更新時に選択肢を更新」属性を有効にします(図2参照)。これにより、文書の更新が強制されている限り、ユーザーが読み込みモードから編集モードに切り替えたときに、キーワード・ドロップダウン式が再計算されます。 図2. ドロップダウン・フィールドのプロパティー![]() 最後に、フォームのPostmodechangeイベントに(図3参照)、ユーザーが読み込みモードから編集モードに切り替えたときに更新を強制する次のコードを含めます。 If source.EditMode Then Call source.Refresh ![]()
ドロップダウン・リストの代わりにボタンおよびpicklistを使用する場合によっては、あまりにもたくさんのドロップダウン・リストがあり、リストのサイズが大きく、使用頻度も高いため、合理的なソリューションとして、主要フォームでドロップダウン・リストの使用を停止する以外の方法が考えられないケースもあります。使用する際の判断方法として、以下の質問を自分自身に問いかけてみます。
次のような回答がよくあります。「一般に、文書は10回ほど読まれますが、編集されるのは数回です。これらの編集のうち、ドロップダウン・リストが使用されるのは1回だけです」もちろん、使用方法に応じて回数は常に変わりますが、これらは検討に値する質問です。文書が頻繁に編集されるのに、検索の必要性がほとんどない場合は、前のセクションの方法では不十分でしょう。実際に、リストは1回しか必要ないのに、ユーザーが編集モードにいるときは常にドロップダウン・リストが再計算されるからです。このような場合はボタンの使用を考慮し、ボタンをpicklistとともに使用することもあります。 ボタンを使用することの利点は、フィールドから検索式を削除できることです。フィールドはドロップダウン・リストではなく、FieldNameの式(その後の例にある式、ProblemCategory)を持つ通常のテキスト・フィールド(多くの場合、作成時の計算結果)にします。混乱を避けるために、読み込みモードのときはボタンを非表示にしますが、編集モードでボタンをクリックすると、リスト2に示す式が使用されます。 リスト2. ボタンの式
@Promptおよび@DbLookupの代わりに@Picklistを使用すると、さらにパフォーマンスを改善できます。この方法の欠点は、ポップアップのレイアウトと背後にあるデータをあまり制御できないことです。例として、リスト3のコードを参照してください。 リスト3. @Picklistの使用
ドロップダウン・リストの代わりにレイアウト領域(またはポップアップ)を使用する検索の規模および頻度に基づき、アプリケーションにとって前のセクションが適切なアプローチであると判断した場合でも、必要な制御が得られないことがあります。このような場合は、ダイアログ・ボックスの使用を考慮します。@式またはLotusScriptによってダイアログ・ボックスを表示し、1つのダイアログ・ボックス内で複数の検索を実行できます。相互に関連する複数の検索がフォーム内にある場合、ダイアログ・ボックスは優れたソリューションになります。たとえば、ユーザーが会社を選択した後は、その会社内の連絡先のリストを表示します。さらに、3番目のドロップダウン・リストには、その連絡先に関する未解決のチケットの件名の行が含まれています。このようなケースでは、前のセクションで説明したようなボタンを配置するとよいでしょう。ただし、リスト4に示すコードを使用します。 リスト4. ダイアログ・ボックスの使用
ここでは各パラメーターの詳細については説明しませんが、開発者用のヘルプ・リファレンスをリスト5に示します。 リスト5. ヘルプ・リファレンス
「(Dialog-CompanyLookup)」フォームで、レイアウト領域を挿入し、@IsDocBeingEditedを考慮せずにすべてのドロップダウン・フィールドを配置できます。ユーザーは、ボタンをクリックし、ダイアログ・ボックスを表示したときにのみ、検索の待ち時間を負担します。 ダイアログ・ボックスを使用するときは、以下の点も考慮してください。
@Evalを使用してコードをクリーンアップするこれまでに、@DbLookup式を使用した@Ifステートメントで埋められた長い@式のコードをときどき見ることありました。このような式は維持しにくく、適切なエラー・チェックを実施したり(「エラーをトラップする」のセクションを参照)、不要な検索を回避することがより困難になる可能性があります。リスト6に示すように、@Evalを使用すると、このような問題を防ぐために役立ちます。 リスト6. @Evalの使用
このコードは、@Ifステートメントに達するまで検索の実行を防止します。つまり、あらかじめすべての検索をセットアップし、これらに適切な変数名を割り当てておきます。そして、必要に応じて@Evalを使用してこれらを呼び出すことができます。
1回の検索で複数のフィールドを取得する異なるデータ・ポイントを取得するために、同じ文書に対して検索を複数回実行している場合は、これらのデータを結合して検索ビューの1つの列に入れることを考慮してください。たとえば、1つの文書から次のデータ・ポイントを取得するものとします。
これを行うために、7つのフィールドを配置して、各フィールドで検索を実行する方法があります。この場合、ContactNameフィールドの式はリスト7に示すコードのようになります。 リスト7. ContactNameフィールドの式
また、ContactPhoneフィールドの式はリスト8に示すコードのようになります。 リスト8. ContactPhoneフィールドの式
他の各フィールドの式も同様です。 7回の検索が同じビュー内の同じ文書に対して実行される場合でも、検索を1回にした方が、7回の検索よりもかなり速いパフォーマンスを得られます。これを実現する1つの方法として、2番目の列にリスト9に示す式が含まれる検索ビューをセットアップします。 リスト9. 2番目の列を持つ検索ビューのセットアップ
次に、フォーム内に BigLookupという非表示フィールドを作成し、リスト10に示す式をこのフィールドに設定します。 リスト10. ContactsByCompanyの検索
今度は、ContactNameフィールドの式はリスト11に示すようになります。 リスト11. ContactNameの検索
ContactPhoneについては、リスト12のコードを参照してください。 リスト12. ContactPhoneの検索
他の各フィールドの式も同様です。 この方法では、以下の2つの点に注意してください。
より速い検索ビューにする@式言語またはLotusScriptのどちらを使用しているかにかかわらず、ビューに対するどのような検索のパフォーマンスも、ビューのサイズとスピードに依存します。ここでは、検索ビューのパフォーマンスを最適化するために役に立ついくつかのヒントを集めてみました。 専用の検索ビューにする 実稼働環境では、ごく一般的な1つのビューの索引を作成する(更新する)ために、約100ミリ秒かかることが明らかになっています。これは、15分ごとにサーバーが各検索ビューを更新するために、1/10秒未満の時間しかかからないことを意味します。もちろん、これらの時間は概算であり、各アプリケーションおよびサーバーによって大きく異なります。しかし、長年にわたるこのデータの研究から、たとえいくつかの検索ビューを追加してもパフォーマンス面での影響はほとんどない、と私たちは確信しています。検索用に1つの専用ビューを追加することにより、そのビューでの検索の待ち時間のパフォーマンスを改善できます。 ビューの設計を合理化する ビューが特定の文書からのデータの検索だけに使用される場合は、ビューの設計から以下の要素を削除できます。
また、カテゴリーを使用することによっても、列をソートし、同じ機能を検索用に提供できますが、ビューの索引作成はより速くなります。 データを合理化する 設計を最小化することに加え、ビューに表示されるデータを最小化することも考慮してください。推奨事項は以下のとおりです。
エントリーの重複を避けるために「索引にユニークなキーを作成する」を使用する これは、特定のビューのサイズを劇的に削減する優れた手法です。検索で大量の重複値が取得され、@Unique (または、LotusScriptの同等の機能)を使用して値を取り出している場合は、この機能を使用できます。このような場合は、「索引にユニークなキーを作成する」オプションを有効にすると(図4参照)、ビューの索引は、検出されたテキスト・ストリングの最初のインスタンスだけを表示します。 ただし、ソート列が複数値フィールドを参照している場合は、このオプションの使用には注意してください。このようなケースでは、Lotus Dominoサーバーのバージョンが6.xの場合は、@Implode(MultiValueField; "~")を使用し、検索式で@Unique(@Explode(@DbColumn(); "~"))を使用して、正しい固有値のセットを取得してください。Lotus Domino 7を使用している場合は、サーバーが論理的に判断してすべての固有値を表示するので、この点は問題になりません。この機能を使用しているビューは、おそらくビューの更新時に通常よりもサーバーの作業が増えるので、索引の速度が多少遅くなります。ビューに表示される文書の数が劇的に削減される場合にのみ、この機能を使用してください。多くのアプリケーションでは、この機能を使用したときにビューのサイズが1/100に削減されると、この機能の価値が生じます。 図4. 「ビューのプロパティー」ダイアログ・ボックス![]()
プロフィール文書を使用する複数のユーザーによって更新されず、更新も頻繁に行われないリストがある場合、これらの値をプロフィール文書に格納すると、値を取得する速度が向上します。プロフィール文書でリストを手動で更新するか、通常の文書でリストを更新し、その後プロフィール文書に移動するかにかかわらず、ユーザーはビューをキャッシュするときよりも効率よく、プロフィール文書をキャッシュできます。このため、繰り返し実行する検索が速くなります。 しかし、複数のユーザーがリストを更新する場合は、おそらくプロフィール文書は適切ではありません。複数の更新によって相互に上書きされ、複製/保存の競合が発生する可能性があるからです。
検索データベースを使用する大規模なアプリケーションでは、個別の検索データベースを使用することがしばしばあります。おそらくこれは、検索するデータがあまりにもたくさんあるため、データと必要なビューをメイン・データベースから切り離すことに意味がある、という考えに基づいていると思われます。これは、確かに一理ありますが、実際のところ、通常は得るものよりも失うものの方が多いでしょう。検索データを個別のデータベースに保持すべき場合と、メイン・データベース内に保持することに意味がある場合のヒントを以下に示します。
問題となるのは、パフォーマンスが最優先され、少数の小さなリストにアクセスする複数のデータベースがあるケースです。このようなケースでは、これらのリストを個別の検索データベースで維持するとよいでしょう。ただし、LotusScriptエージェント(または、Lotus Enterprise Integrator)によって、各メイン・データベースに移植します。これは、維持が面倒になりますが、最高のパフォーマンスを発揮します。
まとめこの記事で紹介したヒントが、新しいアプリケーションをコーディングするとき、または既存のアプリケーションでパフォーマンス問題をトラブルシューティングするときに、新たな手法として利用されることを望んでいます。動的な検索は、ほとんどのアプリケーションで重要な機能として用いられています。それゆえ、動的な検索のパフォーマンスへの影響を最小にすることが、長年にわたって使用される優れたプログラムにするための最良の方法となります。 リソース学ぶために
議論する
筆者について
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||