出直し!! ヘルプ

連載中

連載 終了

2026/01/25

Domino 体力測定:#4)ビュー操作 ④ - ビューの列数と値の取得

背景と目的

前回は実運用に近い検証を目的に、ビューから取得する文書のサイズ(フィールド数)とビュー操作を行うメソッドの特定を測定しました。今回は、ビューに定義されている列数とレスポンスの関係について調査します。


測定環境

測定に使用する PC はこれまでと同じ PCを使用します。また、DB は前回作成したダミーフィールドを 200 個追加したものを使って測定します。

今回のテストでは、ビューに配置するダミーフィールドの数を変化させテストします。利用するビューは次の 5 種類です。

View 名 テスト文書の状態
000 ダミー列なし。
050 ダミー列を 50 個追加(Text_00 ~ Text_31)
100 ダミー列を 100 個追加(Text_00 ~ Text_63)
150 ダミー列を 150 個追加(Text_00 ~ Text_95)
200 ダミー列を 200 個追加(Text_00 ~ Text_C7)


手順

測定手順はこれまでと全く同じとします。ビューに対して 20 回の検索を行い、取得したコレクションからエントリと値を取得する操作を 50 回行います。測定回数は 10 回です。

この測定を View 050 ~ 200 まで順に行います(View 000 については前回の結果を流用)。

なお、テスト項目と測定の項目は「#1)ビュー操作 ① - 測定方法」をご確認ください。


結果

測定結果は次の通りでした。

このままではわかりにくいので、測定時間の推移をテスト項目ごとにグラフ化します。

結果が水平となっているのは、D2、D3、E4、E5 です。それ以外のテスト項目については、列数に比例して処理時間が増えているように見えます。

前回に倣って、値の取得(D1 ~ D3、E1 ~ E3 )に絞ってグラフ化ました。全体の処理時間(折れ線グラフ)も、列数にほぼ比例して処理時間が増えています。また、列数の増加に比例して、GetAllEntriesByKey の方が遅くなっていることが確認できます。


考察

今回の測定結果より推察できるビュー操作の特徴を整理します。


◇ 検索時間の違いはコレクション作成時間?

まず、ビューを検索し検索結果のコレクションを確認する D1 と E1 を比較します。処理時間の比率を確認するとダミー列なしで 1.7 倍、200 個で 1.6 倍の差があり、グラフ化すると次の通りでした。

D1 と E1 の測定では、検索キーの指定、検索結果が同じなので、ビュー検索時間は一定と仮定できると思います。グラフはほぼ水平であることから、コレクション作成時間は、ビューの列数に比例しており、列数が増えるほど GetAllEntriesByKey には不利に働くといえます。


◇ NotesViewEntry クラスのオーバヘッド

E2 の NotesViewEntry 取得の結果を見るとダミー列数に比例して、処理時間が増えています。D2 の NotesDocument の取得はダミー列数に依存せず一定な上、ほぼ 0 なので、エントリ取得という点においても、GetAllEntriesByKey が不利といえます。


◇ 列数の調整で逆転はない

前回のテストで、NotesDocument から値を取得すると文書サイズに比例して遅くなる結果が出ました。今回のテストはその特性が顕著に表れるダミーフィールド 200 個のテストデータで行いました。

ところが、上記 2 点のとおり、検索処理自体に加え、検索結果からエントリを取得する処理でも、列数に比例してコストが発生することがわかりました。

NotesViewEntry??? クラスを使用したオーバヘッドを赤の斜線、NotesDocument 使用時のオーバヘッドを青の斜線で表すと次のようになります。

最も有利なダミー列なしでも赤の部分があまりに大きく、列を増やすにつれてその差が開きます。この結果より、今回のテストケースにおいては、ビューの列数の調整で GetAllEntriesByKey が早くなることはないことがわかります。


◇ ビュー索引を活用

アルゴリズムの評価ではオーダ(Order)とい指標があります。これは、入力データ数に対して処理時間の変化するかを示すものです。

ソートを例にするとデータ数を n とすると、最悪で n2(n の 2 乗)、一般的には n log n の処理時間が必要なります。データがソート済みだった場合に限定すると n となるパターンがあります。オーダ n は、データ件数と処理時間が比例の関係になることを示します。一方、n log n や n2 は、データ件数の増加に従い急激に処理時間が増加します。

今回のテストでは、ビューの列数に依存する D1、E1、E2 はほぼ比例の関係になっています。この結果から、今回テストしたメソッドには、実行時にソート処理を行うような要素は含まれていないと推察できます。

よって、今回テストした処理では、すでにソート済みの索引データを順になぞっているだけであり、比例的な特性(オーダ n)になっていると考えられます

ビューの検索処理が、ビュー索引を活用した構造になっていることの一つの証左と言えますね。


まとめ

前回の検証では、

  • 文書から値を取得する処理は、フィールド数に比例して遅くなる
  • ビューからの値取得では、文書内のフィールド数に依存せず一定
という結果が出ました。

今回の検証では、ビューに必要な列を用意すれば GetAllEntriesByKey の方が早くなる可能性を調査したのですが、結果は、

  • ビューの列数とレスポンスは比例の関係
  • NotesViewEntry??? の処理コストがあまりも高い

ことがわかり、優位に立つことはない結果となりました。

ただ、前回、今回の検証では、検索結果(コレクションのサイズ)を 1000 としていました。実運用では、少々大きすぎるかもしれません。コレクションを小さくした場合や GetEntryByKey ではどうなるかの検証が必要かもしれませんね。


前回 Domino 体力測定


2026/01/20

Int と CInt (LotusScript)

LotusScript で実数を整数に変換する関数として Int と CInt 関数の 2 種類があります。PC が一般的になり始めたころに BASIC 言語を使用していた私には Int の方がなじみがあります。その経験から CInt も同じだろうと無頓着に使用してきましたが、微妙に差があったのでまとめておきます。


丸め方の違い

デザイナーヘルプによると次のように説明されています。

Int 引数に指定した数値に等しいか、それよりも小さい整数値のうち、指定した数値に最も近い値を返します。
CInt 値を Integer データ型に変換して返します。
最も近い整数に丸め、Integer 型の値として返します。

ニュアンスから関数の立ち位置が見えてきますね。Int は整数値に変換することを主題にしてています。CInt は型変換が主題で、その副次的機能として丸め処理が発生しているようです。

そして、小数値の処理である丸め方の表現に違いがあることがわかります。


丸め方の動作検証

それでは実際に処理の違いを確認しましょう。簡単なエージェントを作成し、Int と CInt の差を確認します。

Option Declare

Sub Initialize
   xTest 1.5
End Sub

Function xTest(vdVal As Double)
   MsgBox "Int(" & CStr(vdVal) & ") = " & CStr(Int(vdVal))
   MsgBox "CInt(" & CStr(vdVal) & ") = " & CStr(CInt(vdVal))
End Function

引数を変えながら動作検証した結果は次の通りでした。

1.1 1.5 1.9 2.1 2.5 2.9
Int 1 1 1 2 2 2
CInt 1 2 2 2 2 3

Int は切り捨て処理であることが確認できます。しかし、CInt では 0.5 の処理が単純な四捨五入とは違います。この部分の動作を詳しく確認します。

1.49 1.50 1.51 2.49 2.50 2.51
Int 1 1 1 2 2 2
CInt 1 2 2 2 2 3

プログラミングや統計の世界では、5 ちょうどの扱いを「常に切り上げない」といった仕様が使われることがあり、統計的に偏りが出にくいことを目的としています。

CInt はこの仕様を採用しており、ヘルプでは ”四捨五入” と表現せず、”最も近い整数に丸める” という表現になっているということですね。


負の数の対応

続いては、負の数の処理に関して確認します。

CInt の動作はわかりやすく、単に符号が付いただけの動作になります。

ところが、Int の場合は、絶対値が変化しています。これは、指定した値よりも ”小さい” 最も近い整数となるためです。

-1.49 -1.50 -1.51 -2.49 -2.50 -2.51
Int -2 -2 -2 -3 -3 -3
CInt -1 -2 -2 -2 -2 -3

負の数はビジネスでは使用頻度が低いかもしれません。ただ、頻度が低いと間違い(確認漏れ)が発生しやすいので注意しましょう。


イレギュラーの対応

最後に、これら関数に対してイレギュラーな値を与えた場合の動作について整理します。

テストデータ Int CInt
Integer の範囲を超える数値 77777.7 77777 エラー
数値に変換できる文字列 "2.50" 2 2
数値に変換できない文字列 "14.5 FP1" エラー エラー
日付/時刻 Now 2026/1/18 エラー
Empty な Variant 型変数 0 0
配列 エラー エラー

まず、この結果より Int 関数は名称から Integer 型と思いがちですが、Integer 型を超えた値の処理が可能です。そのおかげで日付/時刻データから時刻部分(小数値)を削除する演算としても使用可能です。

式言語の @TextToNumber で文字列を数値に変換する場合、変換できる範囲で実行する機能があります。例えば "14.5 FP1" は、14.5 に変換してくれるのですが、LotusScript では単純にエラーとなります。

Variant 型変数を引数にする場合には、Empty(変数の宣言だけして値を代入し忘れた状態)は 0 となり、配列やオブジェクトが入っている場合にはエラーとなります。

このような、イレギュラー処理が発生すること自体あまり良いコーディングとは言えないと思います。必要な場合はコメントに記すなど、後から見てわかるようにしておきましょう。


2026/01/16

@Sort は高性能!

@Sort を使えば、リスト値をソートすることができます。 先日、この関数のヘルプを見ていると、さまざまな機能が入っていることに気付いたので紹介します。


@Sort の構文

Domino デザイナーヘルプで @Sort を調べると構文は次のようになっています。

   @Sort( list ; [ order ]; customSortExpression )

今回紹介するのは 2 つ目の引数 order です。

ここにキーワードを指定するとソート動作を詳細にコントロールできるようになります。キーワードは 2 つずつが対になっています。

キーワード(〇:デフォルト) 機能
[ASCENDING] 昇順 / 降順を指定
[DESCENDING]
[ACCENTSENSITIVE] 濁点、半濁点の区別
[ACCENTINSENSITIVE]
[CASESENSITIVE] 大文字、小文字の区別
[CASEINSENSITIVE]
[PITCHSENSITIVE] 全角、半角の区別
[PITCHINSENSITIVE]

デフォルトは ”昇順” かつ ”区別する” になっていて、区別させたくない場合に [???INSENSITIVE] を指定することになります。

キーワードの指定は :(コロン)で複数指定することができるので、さまざまなパターンのソートが実現できるということですね。


動作検証

大文字/小文字、全角/半角については結果が想像できるので、濁点/半濁点を検証します。

まず、キーワード未指定と [ACCENTINSENSITIVE] を指定した場合を比較すると次のようになります。[ACCENTINSENSITIVE] では濁点/半濁点だけでなく、小書きも無視してソートしてくれるようです。

ただ、これだけではひらがなとカタカナには効果がありません。区別なくソートするためには [CASEINSENSITIVE] も指定します。

残念ながら、カタカナの全角/半角については、正しく判定されません。モノは試しと [PITCHINSENSITIVE] も指定してみましたが効果がありませんでした。半角カタカナでは濁点/半濁点が 2 文字になるためだと思われます。


まとめ

今回は @Sort のソート順を指定するオプションについて紹介しました。Notes/Domino は海外の製品ですが、日本語の扱いにここまで対応しているんですね。『ソートといえば文字コード順』と思い込んでいたので、正直驚きました。どのような仕組みで実現しているのか ”中の人” に聞いてみたくなりました。

なお、@Sort のキーワードには [CUSTOMSORT] というものがあります。こちらについては、まだ不明瞭な点もあるため触れませんでした。解説できるぐらい理解できたら、改めてまとめたいと思います。


2026/01/10

Domino 体力測定:#3)ビュー操作 ③ - フィールド数による影響

背景と目的

前回はビュー操作を行う 2 つのメソッドの基本的な特性の測定結果を紹介しました。そのテストでは、テストデータの文書は小さく軽量、検索用のビューもシンプルでした。より実運用に近い検証となるよう、追加でテストしたいと思います。

今回はフィールド数を変化させ、レスポンスにどのような影響があるのか測定します。


測定環境

まず、測定に使用する PC は前回と同じものを使用します。SSD 搭載の今どきの環境の想定です。

フィールド数とレスポンスの関係を調べることから、今回のテスト DB は、一定のダミーフィールドの個数を変化させた5 種類を用意します。ダミーフィールドには、読み込み用と同じ 100 Byte のテストを設定します。

DB 名 テスト文書の状態 文書サイズ
000 前回と同じ。188 Byte
050 000 の文書にダミーフィールドを 50 個追加(Text_00 ~ Text_31)5288 Byte
100 000 の文書にダミーフィールドを 100 個追加(Text_00 ~ Text_63)10388Byte
150 000 の文書にダミーフィールドを 150 個追加(Text_00 ~ Text_95)15488Byte
200 000 の文書にダミーフィールドを 200 個追加(Text_00 ~ Text_C7)20588Byte

参考までですが、ダミーフィールド作成する部分のコードは次の通りです。

      '取得用の値
      s = Right("0000000000" & s, 10)
      s = s & s & s & s & s & s & s & s & s & s
      Call nd.Replaceitemvalue("Text", s)


      'ダミーフィールド作成(viWait は作成する個数)
      For i = 0 To viWait - 1
         Call nd.Replaceitemvalue("Text_" & Right("00" & Hex(i), 2), s)
      Next

250 個作成しようとするとエラーとなりました。9.0.1 FP8 以降で対応した LargeSummary を有効化する必要があるので、今回は 200 個までで検証としています。


手順

測定手順は前回と全く同じです。ビューに対して 20 回の検索を行い、取得したコレクションからエントリと値を取得する操作を 50 回行います。測定回数も同じ 10 回とします。

この測定を DB 050 ~ 200 まで順に行います(DB 000 については前回の結果を流用)。


結果

測定結果は次の通りでした。

横軸を文書サイズに設定し、各測定項目と DB 毎の結果をグラフ化すると次の通りです。

D3、E5 のグラフは右肩上がりの直線となっており、フィールド数に比例しているといえます。それ以外のグラフは水平で、フィールド数の影響は受けないことがわかります。


考察

今回の測定結果より導くことができるビュー操作の特徴を整理します。


◇ ビュー検索はフィールド数に依存しない

ビューの検索を行う D1、E1 ともフィールド数増減の影響を受けず、ほぼ一定の処理時間となっています。また、コレクションからエントリを取得する処理(D2、E2)も同様です。さらに、NotesViewEntry から NotesDocument を取得する処理(E4)も一定です。

これらの結果より、コレクション、コレクション内のエントリ操作、NotesDocument オブジェクトの取得までは、文書単位で処理をしており、文書内のフィールド数には依存しないことがわかります。

ここまでの処理においては、1 文書は 1 文書で、その内部の状態は関係ないということですね。


◇ 値取得はフィールド数に依存

NotesDocument から値を取得する処理(D3、E5)では、フィールド数に比例して、処理時間が増加します。

今回の検証データは、読み込む Text フィールド作成後、ダミーフィールドを追加しました。内部的に作成順で記録されていると仮定すると、Text フィールドは文書のほぼ先頭に存在します。それでも、フィールド数に比例した結果になるということは、フィールドにアクセスしたタイミングで、すべてのフィールドの一覧を作成していると考えられます。

アプリの仕様変更を行うと無用となるフィールドが発生することがあります。実害がないので、ついつい残しがちなのですが、パフォーマンスという観点でいうと削除しておいた方がよさそうですね。

なお、今回の検証では、ダミーフィールドのサイズを固定したため、フィールド数 ≒ 文書サイズとなっています。今回の特性の要因が、文書サイズである可能性が残ります。


◇ 逆転の可能性

文書から値を取得することだけに限定すると、D1 ~ D3、E1 ~ E3 までで実現できます。この部分に絞って、結果を整理すると次のようになります。

GetAllDocumentsByKey(青)のグラフは右肩上がりで、GetAllEntriesByKey(オレンジ)は水平です。ダミーフィールドなしでは、ほぼダブルスコアですが、200 個ではずいぶん差が小さくなっています。さらにフィールドを増やしたテストを行えば逆転することがあるかもしれません。

なお、今回の検証では、ダミーフィールドのサイズを 100 バイトにしています。そのため 200 個以上フィールドを増やせませんでした(Summary フィールドの上限)。セットする値を小さくし、フィールド数を増やした場合や Domino 9.0.1 FP8 以降で対応した LargeSummary 環境での特性も気になりますね...


まとめ

今回の検証では、GetAllEntriesByKey で検索し ColumnValues で値を取得する限りにおいては、フィールド数に依存せず同じレスポンスになることがわかりました。逆に NotesDocument オブジェクトを介して値を取得するとフィールド数に比例した処理時間が必要とります。ただ、ここまでの検証では GetAllEntriesByKey の方が早くなるパターンは見つかっておらず、GetAllDocumentsByKey が優勢の状態に変わりはありません。

なお、今回の検証で不明瞭な点は、次の通りです。機会があれば検証したいと思います。

  • もっと大量のフィールドが存在する場合、処理時間が逆転することはあるか?
  • フィールドサイズの大小で変化はあるか?
  • LargeSummary 環境において特性に変化はあるか?


前回 Domino 体力測定