出直し!! ヘルプ

連載中

連載 終了

2024/02/28

設計引継ぎの動作 ③:アプリケーションの設計の置換

テンプレートからアプリケーションに対して設計を反映する機能(操作)の最終回です。今回は『設計の置換』を紹介します。


設計の置き換えの操作

アプリケーションアイコンの右クリックメニューから [アプリケーション] - [再設計] を選択します。

続いて、テンプレートを選択する画面が表示されますので、サーバとテンプレートを選択します。

【置換】ボタンをクリックすると、アプリケーションの設計が選択したテンプレートに置き換わります。実行が終了すると、アイコンが更新されました。処理内容はステータスバーに表示されますので、通知(鈴のアイコン)から詳細が確認できます。

アプリケーションを開くとフォームやビューなど設計が選択したテンプレートに置き換えられたことが確認できます。


設計の置き換えの機能

設計が置き換えられるのは前述の通りですが、その結果を確認しましょう。

プロパティを開きテンプレートを確認すると引き継ぐテンプレート名が変更されています。設計の置換では、テンプレートから設計を反映するだけでなく、引き継ぐテンプレートの設定まで行ってくれるのが特徴です。以降は、再設計や Design タスクで設計が反映できるということですね。


テンプレートを引き継いで設計

新規アプリケーション作成時や設計の置き換え画面には『テンプレートを引き継いで設計』というチェックボックスが存在します。

このチェックを外した場合は、アプリケーションのプロパティからテンプレートの設定が解除されます。再設計や Design タスクで移行設計を更新したくない場合に使用できますね。

もちろん、アプリケーションのプロパティから上記設定を手動で削除するだけでもテンプレートとの関係は解除できます。


設計の置き換えと引継ぎ

設計の置き換えはテンプレートの設計に入れ替える機能でした。この機能は、一般的にはアプリケーションのバージョンアップの場合に利用します。

例えば次のような手順です。

  1. 運用中のアプリをローカルなどにコピーする
  2. デザイナーで設計を変更する
  3. テンプレートを作る(設計を ntf としてコピー)
  4. 運用中のアプリの設計を置き換える

設計の置換後もテンプレートから設計を反映したい場合は、テンプレートの配置とテンプレート名の設定を別途行い、Design タスクに任せることになります。

設計の置き換えと設計の引継ぎは別機能として考えるとわかりやすいと思います。


ところで、最初の事例のように、全く違う設計に変更することもできます。ただ、文書はそのままなので、機能と文書が合致せずアプリとしては成立しなくなります。”設計の置換” 操作としては正しい動作なのでエラーは発生しません。

『見たことのない画面になってる!』と突然ユーザから言われる。長年ノーツ担当すると何度かは経験する『ノーツあるある』です...

ご注意ください。


2024/02/26

設計引継ぎの動作 ②:アプリケーションの再設計

前回は、テンプレートからアプリケーションに対して設計を反映する Design タスクを紹介しました。今回はノーツクライアントから実行できる操作『アプリケーションの再設計』についてまとめます。


アプリケーションの再設計

アプリケーションアイコンの右クリックメニューから [アプリケーション] - [再設計] を選択します。

するとサーバの選択が求められますので、テンプレートが存在するサーバを指定します。

再設計を実行すると、ステータスバーに結果が表示されます。通知を開き、ステータス タブから詳細が確認できます。


指定したサーバにテンプレートが見つからない場合には、エラーメッセージが表示されます。


タスクと再設計の違い

Design タスクと再設計はどちらもテンプレートから設計を反映するための機能です。それぞれの違いについてまとめると次のようになります。


再設計 Design タスク
実行場所 クライアント サーバ
引継ぎ元 別のサーバやローカルのテンプレートも利用可能 サーバ内のテンプレートのみ
対象 単一のアプリケーション サーバ内のすべてのアプリケーション
タイミング 手動 自動(手動実行も可能)

繰り返しになりますが、どちらの機能であっても、アプリケーションとテンプレート名が合致している必要があります。この関係が成立していない場合には設計は反映されません。


2024/02/24

設計引継ぎの動作 ①:デザインタスク

テンプレートからアプリケーションに対して設計を反映する動作の一つである『デザインタスク』の仕組みについて整理します。


デザインタスクとは?

デザインタスク(Design)は ”タスク” ですので Domino サーバで稼働するプログラムです。実行するとサーバ内のすべての DB を対象に実行され、テンプレートとの設計の相違を修正する動作を行います。

処理対象となるアプリケーションは、テンプレートに設定されている名称と合致している必要があります。


Design タスクが実行されるとサーバにログが記載されます。まず、開始と終了は次のようなメッセージが表示されます。

2024/02/23 09:02:04 Database Designer started
  ・・・
2024/02/23 09:02:06 Database Designer shutdown

さらにログには設計要素を修正した記録が出力されます。サンプルとして、更新 / 追加 / 削除 した場合のログを抜き出すと次の通りとなります。

2024/02/23 09:02:05 (コメント\IDB) をデータベース 新規アプリケーション へテンプレート Domino ブログ (12) から更新します。
2024/02/23 09:02:05 (コメント\ID) をデータベース 新規アプリケーション へテンプレート Domino ブログ (12) から追加します。
2024/02/23 09:02:05 (コメント\IDB_add) をデータベース 新規アプリケーション から削除します。

アプリケーションとテンプレートともアプリケーション名称で記録されています。ファイル名やパス、テンプレート名で記載してくれた方が明確になるんですけどね...


エラーの表示

例えば、アプリケーションのプロパティでテンプレート名を間違えたとします。

この状態でデザインタスクを実行すると、テンプレートを発見できないことから警告メッセージが表示されます。

2024/02/23 09:19:38 警告:データベース 'C:\Lotus\Domino\data\NewApp.nsf' で使用されている設計テンプレ ート 'xxxStdDominoBlog/ja' を見つけることができません

エラーの場合は、問題となったテンプレート名やアプリケーションのファイルが明確に表示されます。これなら対応がしやすいですね。


タスクの定期的な実行

Domino サーバのデフォルトでは、午前 1 時に Design タスクが実行されます。これは、サーバの notes.ini に以下の行が記述されているからです。

ServerTasksAt1=Catalog,Design

ServerTasksAt1 は午前 1 時に実行するタスクを設定するエントリです。上記設定では、Catalog タスクと Design タスクの 2 つを実行するということですね。

実行時間を午前 2 時に変更したい場合には ServerTasksAt2 にタスク名のDesign を記述すればよいということになります。


タスクの手動実行

サーバに対して次のコマンドを入力すると即座に Design タスクを実行します。必要の応じて手動でタスクを実行できます。

> load design


2024/02/22

テンプレートの使用と設計の引継ぎ

テンプレートを使用して新規のアプリケーションを作成する画面では、『テンプレートを引き継いで設計』というオプションがあります。今回はこの設定を材料にテンプレートとアプリケーションの関係について確認します。


テンプレートの引継ぎとプロパティ

アプリケーション作成時に『テンプレートを引き継いで設計』にチェックを入れると、作成されたアプリケーションのプロパティでは『テンプレートから設計を引き継ぐ』にチェックが入ります。逆にチェックを外して作成するとプロパティにもチェックが入りません。

また、『テンプレートから設計を引き継ぐ』にチェックがある場合、テンプレート名に値が入っています。

テンプレート名はテンプレート(ntf)のプロパティを開き『マスターテンプレートのデータベース』というチェックチェックボックスの下に記述されています。この名称とアプリケーションのテンプレート名が合致することにより、テンプレートとアプリケーションの引継ぎ関係が成立する仕組みになっています。

アプリケーション作成時には、テンプレートのタイトルとファイル名しか表示されないことから、テンプレート名との関連がわかりにくいので、覚えておきましょう。


引継ぎ時の動作

『テンプレートから設計を引き継ぐ』状態にあるアプリケーションでは、テンプレートの設計と同じになるように動作します。例えば、アプリケーションの設定を更新した場合はテンプレートの設計に戻ります。逆に、テンプレートの設計が更新された場合は、アプリケーションの設計にも反映されます。

Notes/Domino において、このような ”設計を引き継ぐ” 動作(操作)はいくつかあります。

  • デザインタスク(Domino サーバ)
  • アプリケーションの再設計(Notes クライアント)
  • アプリケーションの設計置換(Notes クライアント)

それぞれ少しずつ機能が違うので、次回以降に順次まとめます。

 

2024/02/20

アプリケーションの作成とテンプレート

Notes/Domino のデータファイルは次の 2 つに大別されます。

種類 拡張子
1 アプリケーション nsf
2 テンプレート ntf

アプリケーションは通常利用するアプリケーションそのもので、テンプレートはアプリケーションの設計要素(機能)だけを保持するアプリケーションの ”ひな形” にとなります。テンプレートの名称の通りですね。

では、順にテンプレートの利用方法を確認しつつ、テンプレートの役割を確認しましょう。


アプリケーションの新規作成

新規アプリケーションの作成時に、テンプレートを指定することができます。

例えば、『Domino ブログ(12)』というテンプレートを選択して、アプリケーションを作成すると、設計要素が入った文書が空っぽのアプリケーションが作成されます。『Domino ブログ(12)』テンプレートの設計要素を丸ごとコピーした新規アプリケーションが作成されたということですね。

テンプレートを指定しない(『-未入力-』を選択)場合、設計要素も空っぽのアプリケーションが作成されます。ですので、テンプレートの指定は必須ではありません。


テンプレートと ACL

新規作成したアプリケーションの ACL を確認すると初期設定ができていることが確認できます。『Domino ブログ(12)』テンプレートの場合だと、Anonymous のエントリが作成されていてアクセス権が「作成者」に設定されています。また、ロールが定義された状態になっています。

このコントロールはテンプレート側の ACL の設定で行われています。テンプレートもワークスペースにアイコンを表示することが可能です。例えば、『Domino ブログ(12)』テンプレートを追加するのであれば、アプリケーションを開く画面でサーバを指定して、ファイル名を手入力します。

ファイル名は、アプリケーションの新規作成画面でテンプレートを選択する画面やDomino Administrator クライアントなどで確認できます。


ワークスペースにテンプレートが追加されたら、ACL を確認します。すると [Anonymous] というエントリがあり、新規作成されたアプリケーションの Anonymous と同じ設定となっていることが確認できます。

テンプレートからアプリケーションを作成した場合、エントリの [ ] をとり、ACL の初期値として登録する機能があります。これを利用して、ACL の初期設定をしていたんですね。

ちなみに、[ ] のない通常のエントリは、テンプレート自身の ACL として利用されています。


テンプレートもアプリケーション!?

上記の通り、テンプレートに対しても ACL の設定があり、設定どおりに機能します。そして、テンプレート内にも文書を登録することはできます。ですので、通常と同じアプリケーションのように利用することは可能です。構造上はアプリケーションとテンプレートに差はありません。

だからといって、テンプレートをアプリとして利用しても、運用が不明瞭なるだけです。あくまで、サンプルデータを入れてテンプレートを機能を明示する程度のものと考えましょう。また、運用するアプリを作成する際は、nsf と ntf は似ているので打ち間違えないように注意が必要ですね。

機能は正しく使わないと使いにくくなるだけですから...


2024/02/18

リッチテキスト:#17)編集中のリッチテキスト

ノーツの特徴的な機能であるリッチテキストを LotusScript で操作する方法を紹介している連載『リッチテキストの基本操作』の 17 回目です。

今回は編集中のリッチテキストのアクセスについてまとめます。記事を書くにあたり検証した結果、私自身も新発見がありました。ですので、今回は解説というよりは、実験レポートのような位置づけとなります。ご了承ください。


入力チェック

編集中のリッチテキストに対してアクセスするシーンで最初に思い浮かぶのが入力チェックです。今回は事例として、添付用のフィールドに添付ファイルが存在するかを確認してみます。

機能的には、指定したリッチテキストフィールドに添付ファイルがあればそのサイズを返す関数を作成します。添付が複数ある場合、その合計サイズを返すものとし、0 を返した場合は添付ファイルが存在しないと判定できる関数です。

作成した関数は次の通りです。

Function xGetSize(Byval sFldName As String) As Long
   Dim nuiw As New NotesUIWorkspace
   Dim nuid As NotesUIDocument
   Dim nd As NotesDocument
   Dim ni As NotesItem
   Dim nrti As NotesRichTextItem
   Dim nemb As NotesEmbeddedObject

   Dim lSize As Long
   Dim vTmp As Variant
   Dim i As Integer

   Set nuid = nuiw.CurrentDocument
   Set nd = nuid.Document

   lSize = 0

   '添付ファイルを認識させる
   Call nuid.Refresh(True)

   'フィールドを取得
   Set ni = nd.GetFirstItem(sFldName)
   If ni.Type = RICHTEXT Then
      'リッチテキストなので添付ファイルをチェック
      Set nrti = ni
      vTmp = nrti.EmbeddedObjects
      If Not (IsEmpty(vTmp)) Then
         For i = 0 To UBound(vTmp)
            '埋め込みオブジェクトを順に取得
            Set nemb = vTmp(i)
            If nemb.Type = EMBED_ATTACHMENT Then
               lSize = lSize + nemb.FileSize
            End If
         Next
      End If
   End If

    xGetSize = lSize
End Function

続いてフォームにアクションボタンを作成して、上記関数とメインルーチンを記述します。

Sub Click(Source As Button)
   Msgbox xGetSize("Body"), 64, "RichText #17"
End Sub

実行すると次の通りフィールド内の添付ファイルのサイズの合計が返されます。8409 バイトのファイルを 2 つ貼り付けたので正しく取得できています。

Refresh メソッド

この関数でポイントとなるのが、リッチテキスト内部に Resresh メソッドをコールしている部分です。

   '添付ファイルを認識させる
   Call nuid.Refresh(True)

デザイナーヘルプによると Refresh メソッドには引数があります。

Refresh (NotesUIDocument - LotusScript®)

LotusScript では、UI に表示中のオブジェクトをフロントエンドと呼びます。Notes Object Class では、NotesUI????? クラスとして定義されています。そうでない場合はバックエンドと呼び、Notes????? クラス(UI が付かない)として定義されています。

NotesDocument クラスや NotesRichTextItem クラスなど一般の処理に利用するクラスばバックエンドです。入力チェックの作成などにおいては、NotesUIDocument から NotesDocument を取得することになります。

   Set nd = nuid.Document

リッチテキストフィールドでは、通常、バックエンドに渡されるのは文書として保存されている状態のものが渡されます。よって、編集前の状態であったり、入力しているのに空となったりします。

前置きが長くなりましたが、Refresh メソッドの引数で True を指定すると、リッチテキストのフロントエンドがバックエンドに反映されるというわけです。


エージェントでは使用できない?

ところでこのプログラムをエージェントに移設して、アクションボタンのシンプルアクションでエージェントを実行するようにしてみました。文書を編集してからボタンをクリックすると次のようなエラーが発生しました。

プログラムは全く同じなので、エージェント化した場合の制約だと思われます。

前回 発生したエラーもエージェント化が原因だったということですね...


まとめ

今回の実験で編集中のリッチテキストを扱うには、

  • Refresh(True) の実行が必要
  • エージェント化するとエラーが発生する

という点がポイントであることがわかりました。


ところで、私はつい先日まで『リッチテキストの編集はいったん保存しないとフロントエンドでは扱えない』と思い込んでいました。Refresh メソッドに引数が増えたことは、気が付いていたのですが、全く調べず放置していました。引数が増えたのは、リリース 5 か 6 のようなので、20 年以上も放置していたことになります。

気づいた時点で調べる!技術者にとって大切ですね。今後はこのようなことがないよう気を付けようと思います...

前回 リッチテキストの基本操作


2024/02/16

DXL Step-by-Step:#28)DXL を利用したサンプルアプリが公開されました!

本日、2024 年 2 月 16 日に東京で『ノーツコンソーシアム FESTA 2024』が開催されました。プログラムの1コマに私がお世話になっている地区別研究会である『大阪研究会』(大阪研)の成果発表を行いました。


その成果の一つとして、DXL を利用したサンプルアプリがあり、設計公開の nsf として公開されました。DXL 部分は私が担当し、この連載の内容が多く含まれますので、紹介させていただきます。


サンプルアプリ

まずサンプルアプリは、大阪研仲間の 林 哲司 さんのブログ『Notes開発者のためのXPagesデザインレシピ』で公開されており、nsf としてダウンロードが可能です(設計公開)。

公開されたサンプルアプリは、Youtube から動画の一覧を取得、動画のサムネイル付きでノーツのビューに表示するというアプリです。

機能的には、

  1. Youtube から動画の情報やサムネイル画像を取得
  2. イメージリソースにサムネイル画像を登録し、ビュー表示に利用
  3. イメージリソースをリッチテキストにインラインイメージで貼り付け

というようなことを実現しています。開発技術的には、

  • WebAPI 連携
  • DXL

を利用しており、1 と 2 で WebAPI 連携、2 と 3 で DXL を使用しています。

ビューなどノーツの基礎技術を使いつつ、ノーツ臭のしないアプリに仕上がっています。ぜひともダウンロードしてお試しください。


WebAPI 連携と DXL を使いこなせば、これまで ノーツだから... と諦めていた機能が実現できることがあります。そんな可能性を感じていただけるはずです。


DXL ライブラリの構成

私が担当したのは、DXL を使用した部分で、DXL_Tiny というスクリプトライブラリにまとまっています。このライブラリには、公開されている(Public の)関数が 3 つあり、それぞれ次のような機能を提供しています。

関数 役割
ImageResource_New イメージリソースの登録
ImageResource_Remove イメージリソースの削除
InlineImage_Replace リッチテキストにイメージリソースをインラインで表示

関数の内部は、この連載『DXL Step-by-Step』で紹介した内容を再構成して作成しました。関数ごとに参考となる回のリンクをまとめます。もちろん、全体的に関連するのですが、直接関連するものを 、間接的なものを △ で表しています。

コードの確認にあたり、参考にしてください。

# タイトル
8 イメージリソース(設計要素)の DXL
9 DXL 内の画像のダウンロード
10 イメージリソースの新規作成
11 イメージリソースの DXL の注意点
12 イメージの形式と DXL の関係
13 イメージの形式とサイズの取得
14 文書のフィールドと DXL
15 リッチテキストを構成する基本要素
21 段落内に入るオブジェクト
26 イメージリソースの DXL
27 イメージリソースの表示


前回 DXL Step-by-Step


2024/02/14

リッチテキスト:#16)テキストの抽出

ノーツの特徴的な機能であるリッチテキストを LotusScript で操作する方法を紹介している連載『リッチテキストの基本操作』の 16 回目です。

前回は、メールの本文からリッチテキストを抽出するサンプルプログラムを紹介しました。今回はテキスト成分を抽出する方法をまとめます。


サンプルの動作

リッチテキストに適当なコンテンツを入力した文書を用意し、サンプルのエージェントを実行するアクションボタンを配置します。

アクションをクリックすると次のように、リッチテキストからテキスト成分だけを抽出してメッセージボックスで表示します。


サンプルのエージェント

作成したコードは以下の通りです。

NotesRichTextItem オブジェクトの Text プロパティを取得しているだけです。たったこれだけのコードで、リッチテキストからテキスト成分を取得できています。改行や表も体裁よく再現されているのでなかなか高性能ですね。

Option Declare

Sub Initialize
   Dim nuiw As New NotesUIWorkspace
   Dim nuid As NotesUIDocument
   Dim nd As NotesDocument
   Dim ni As NotesItem
   Dim nrti As NotesRichTextItem

   Set nuid = nuiw.CurrentDocument
   Set nd = nuid.Document

   Set ni = nd.GetFirstItem("Body")
   If ni.Type = RICHTEXT Then
      Set nrti = ni
      MsgBox nrti.Text, 64, "RichText #16"
   End If
End Sub

前回、少し複雑なサンプルを紹介したので順序を間違えたかもしれませんね...


入力中は取得できない!?

ところで、この文書を編集し、文字を追加してから、このエージェントを実行すると『文書が保存されていません』とエラーが発生します。

テキストや数値フィールドは、入力したてのデータにも簡単にアクセスできます。しかし、リッチテキストフィールドには少し制限があるようですね。


前回 リッチテキストの基本操作 次回


2024/02/12

@DbLookUp の効率的な利用

@DbLookup は便利な関数です。ただ、頻繁に使用するようになると、フォームを開くのが遅くなったり、計算結果の動作が遅くなったと感じることがあります。これは、@DbLookup 1 回の実行時間は小さなものでも、多数の @DbLookup の実行時間が積み重なりレスポンスの悪化につながっていることになります。

今回は、この対策として @DbLookup 関数の数を減らす方法についてまとめます。


郵便番号検索の事例

入力した郵便番号から住所を検索する処理を例とします。入力した郵便番号を @DbLookup で検索して住所を表示しています。

実現するためには、郵便番号マスタに次のような検索用ビューを作成します。検索される 1 列目はソートしておく必要があります。

フィールドは入力した郵便番号 Zip の値を使用して先ほどのマスタビューを検索します。そして取得したい値がある列を指定することになります(”計算結果” フィールド)。下図では、県名(カナ)を取得するので 2 列目を取得しています。


@DbLookup("Notes":"NoCache"; Srv: FP; "SchZip"; Zip; 2)

なお、Svr と FP のフィールドには、郵便番号マスタのサーバとファイルパスが指定されている前提です。


ここで問題となるのが各フィールドの設定です。今回の事例では、住所1~3の漢字とカナの計 6 フィールドがあり、取得する列の違うほぼ同様の式を記述しています。これは、キーが同じ検索を 6 回実行していることになります。


検索回数の削減

このような場合に活用できる技を紹介します。

まず検索用ビューに1列追加し、所得したい値を特定の文字列で区切る式を記述します。


kana1 + "@" + address1 + "@" + kana2 + "@" + address2 + "@" + kana3 + "@" + address3


続いて、フォームの変更です。Kana1 フィールドの式を変更して、先ほど作成した列を取得します。その結果を区切り文字で分離して、それぞれのフィールドにセットします。


xAns := @DbLookup("Notes":"NoCache"; Srv: FP; "SchZip"; Zip; 8);

@SetField("Addr1"; @Word(xAns; "@"; 2));
@SetField("Kana2"; @Word(xAns; "@"; 3));
@SetField("Addr2"; @Word(xAns; "@"; 4));
@SetField("Kana3"; @Word(xAns; "@"; 5));
@SetField("Addr3"; @Word(xAns; "@"; 6));
@Left(xAns; "@")

@Word は、指定した分離記号を使って、部分文字列を取得する関数です。

@Word(xAns; "@"; 2)

この式では、文字列 xAns を "@" で区切って 2 つ目を取得するという指定になります。検索結果が次の通りの場合、赤字の部分を取得することになります。なかなか便利な関数ですね。

トツトリケン@鳥取県@トツトリシ@鳥取市@ハマサカヒガシ@浜坂東

そして、その取得した値を @SetField 関数で目的のフィールドにセットしています。


なお、この変更で、残り 5 つのフィールドの式は不要になります。”作成時の計算結果” に変更して、初期値が null となるよう変更しておきましょう。


このように記述することにより、6 フィールドの分の値を 1 回の @DbLookup で取得できます。


2024/02/10

ReCache ってなんだろう?

前回は @DbLookup や @DbColmun のキャッシュの動作について検証しましたが、ReCache と NoCache の違いまでは発見できませんでした。NoCache はその名の通り、キャッシュせずに最新の結果を取得するのだとわかります。そこで、今回は ReCache についてもう少し詳細なテストを行い、挙動を見極めたいと思います。

前回のマスタ DB とフォームをテスト環境として使用しながら、いくつかの実験を行います。ReCache については、私自身使用経験がなかったので、興味津々です(笑)


テスト①:ビューをキャッシングしているのか?

ビューに表示される結果は、索引として保持されています。これをキャッシングしていて、ReCache で更新できるのではないか?という仮説のテストを行います。

今回のフォームでは住所の 6 フィールドはすべて SchZip ビューにアクセスしています。そこで、最初のフィールドにだけ ReCache を指定して、残りのフィールドはキャッシュから取得するようにしてみました。

この状態で実行して、一番下のフィールドが最新の値を取得したなら仮説の証明となります。

エージェントを実行すると Addr3 の値は更新時刻とは一致しませんでした。この結果より、この仮説は間違いであり、ビューをキャッシングしているのではないことがわかりました。


テスト②:検索結果(文書)をキャッシングしているのか?

@DbLookup の結果をビュー列からの取得ではなく、フィールド名を指定した取得に変更します。そして、前段の Kana2 フィールドの @DbLookup で ReCache を行います。

キャッシングの機能が文書をキャッシュしているなら、Addr3 フィールドでも最新の値が取得できるのではないか?という仮説の検証です。

この検証も結果は NG で、文書をキャッシングしているのではないようです。


テスト③:同じ検索結果の場合

一体何をキャッシュしているのかよくわからないので、式を同じにしてテストしてみます。ただ、1 フィールド Kana2 だけ、ReCache を指定してテストします。

結果は、次の通り ReCache を指定したフィールド以降は正しく取得できています。やっとキャッシュっぽい動作を確認できました...


テスト④:キャッシュしているのは列?

最後のテストは複合パターンです。

テスト③ と同様のパターンで検索する式をいったん揃えます。指定する式はテスト①と同様列番号を指定するタイプに戻しました。そのうえで、Kana2 だけ別の郵便番号を ReCache で取得します。列全体がキャッシュされているならこのフィールド以降が最新の値となります。

さらに追加のテストで、Kana3 は Addr3 と同じ式ですが ReCache で取得します。これはテスト③と同様のテストですね。

このテストケースで実行したところ、結果は以下の通りで、Addr2 の値はキャッシュから取得されました。また、Kana3 で実行した ReCache の効果で、Addr3 は最新の値となっています。


まとめ

この結果より、キャッシュは検索条件が同じで、取得する列(値?)が同じ場合に限りキャッシュしているようです。実アプリのおいて、同じ検索で同じ値を複数取得するシーンが思い浮かばないので ReCache の価値や使いどころは残念ながらわかりません。

現時点では、

  • マスタの更新頻度が高い場合や最新の値が必要な場合には NoCache を指定
  • 更新頻度が低い場合や追加型のダイアログフィールドのように選択肢を提供するだけの機能の場合にはキャッシュさせてもよい

という程度の使い分けでよいかな という判断をしました。今後、この認識にアップデートがあれば改めて記事にしたいと思います。


ところで、キャッシュは、レスポンス(パフォーマンス)向上が一般的な目的となります。次回は、キャッシュの設定と効果について検証したいと思います。


2024/02/08

@DbLookup とキャッシュの動作

@DbLookup や @DbColmun には、キャッシュをコントロールする機能があります。以下の構文の赤字の部分ですね。

@DbLookup(class:cache; server:database; view; key; columnNumber; keywords)

@DbColumn(class:cache; server:database; view; columnNumber)

今回はこの機能が実行結果にどのように作用するのか検証した結果をまとめます。


検証環境とアプリ

多少のバージョンの違いで動作に差が出るような検証ではありませんが、検証に使用した Notes/Domino の環境は次の通りです。

  • Notes 12.0.2 FP2
  • Domino 14.0

続いて検証アプリです。

まず、@DbLookup で検索されるアプリは以前『Project KEEP 体験:#3)郵便番号検索 - 準備作業』で作成した郵便番号マスタを使用しました。郵便番号を検索するためのビュー SchZip を追加作成しています。

@DbLookup で検索しますので 1 列目は郵便番号でソートしています。


テストフォームは、マスタとは別の nsf に作成します。作成するフィールドは次の通りです。今回はテストですので、固定の郵便番号を検索して結果を取得するものとします。各フィールドの種類は "テキスト" で "作成時の計算結果" とします。

ラベル フィールド名
郵便番号 Zip "6820948"
住所1 Kana1 式① (n = 2)
Addr1 式① (n = 3)
住所2 Kana2 式① (n = 4)
Addr2 式① (n = 5)
住所3 Kana3 式① (n = 6)
Addr3 式① (n = 7)

◇ 式①

@DbLookup(Mode; Srv: FP; "SchZip"; Zip; n)

また、上記式で使用している 3 つのフィールドは次の通りとし、フォーム上部に配置します。

フィールド名 オプション
Srv 非表示 @Subset(@DbName; 1)
FP 非表示 マスタDBのファイルパスを文字列で指定
Mode 複数値
非表示
xTest1 := "";
xTest2 := "Notes":"ReCache";
xTest3 := "Notes":"NoCache";

xTest1

なお、Mode の式の赤字部分を xTest2 に変更すると "ReCache" のテストができるようにしています。こうしておくと、テストが容易ですね。


テストエージェント

今回はキャッシュのテスト行うので、マスタ文書を更新した後にフォームを開き、更新後のマスタを取得できるかを確認します。

そこで、エージェントを作成して以下の処理を一気に実行します。

  1. 検索されるマスタデータに処理時刻を書き込む
  2. フォームを表示して @DbLookup を実行(1 で更新した郵便番号の値を取得)
  3. 文書内にマスタデータの処理時刻を表示

実行すると次のように時刻を書き込みます。

その後開いたフォームにおいて、キャッシュから値を取得すると 2 と 3 の値が異なることとなるという算段です。


作成したエージェントは次の通りです。

Option Declare

Private xns As NotesSession
Private xndb As NotesDatabase
Private Const xcFP = "zipcode.nsf"
Private Const xcZip = "6820948"

Sub Initialize
   Dim nuiw As New NotesUIWorkspace
   Dim nuid As NotesUIDocument
   Dim nd As NotesDocument
   Dim s As String

   '初期化
   Set xns = New NotesSession
   Set xndb = xns.CurrentDatabase

   ' 1. マスタに処理時間を書き込む
   s = xUpdateZip()

   ' 2. テストフォーム表示し、@DbLookup を実行
   Set nuid = nuiw.ComposeDocument(xndb.Server, xndb.FilePath, "DbLookupTest")

   ' 3. 処理時刻を表示
   Set nd = nuid.Document
   Call nd.ReplaceItemValue("Time", s)

End Sub

Function xUpdateZip() As String
   Dim ndbZip As NotesDatabase
   Dim nv As NotesView
   Dim nd As NotesDocument
   Dim s As String

   'テストのマスタを取得
   Set ndbZip = xns.GetDatabase(xndb.Server, xcFP, False)
   Set nv = ndbZip.GetView("SchZip")
   Set nd = nv.GetDocumentByKey(xcZip, True)

   '処理時刻を付加
   s = Format(Now, "hh:nn:ss")
   Call xUpdateZip_Address3(nd, s)

   '処理時刻を返す
   xUpdateZip = s
End Function

Function xUpdateZip_Address3(vnd As NotesDocument, ByVal vsTime As String) As String
   Dim v As Variant

   '時刻を付加して保存
   v = vnd.address3(0)
   v = Split(v, "_")
   ReDim Preserve v(1)
   v(1) = vsTime
   vnd.address3 = Join(v, "_")

   Call vnd.Save(True, False)
End Function

エージェントの最後で、処理時刻を文書の Time フィールドにセットしています(赤字部分)。これを表示するためにフォーム上にも Time フィールドを作成しておきます。

完成したフォームのイメージは次の通りです。


キャッシュの検証

まずは、キャッシュが有効な場合をテストします。Mode フィールドの計算式の最後を xTest1 となっていることを確認し、エージェントを実行します。

キャッシュの都合で一致することもありますが、ほとんどの場合、時刻にずれが発生します。更新したてのデータではなく、キャッシュ内の過去のデータを取得していることがわかります。


続いて、Mode フィールドの計算式の最後を xTest2 と修正し ReCache のテストを行います。結果は次の通り、時刻が一致しました。キャッシュを更新してから取得していることがわかります。

この結果は NoCache の場合も全く同じでした。


まとめ

この検証結果より、最新データを必ず取得したい場合には、キャッシュしないように設定する必要があることがわかりました。

ただ、今回のテストでは、ReCache と NoCache の違いは見つかりませんでした。次回はこの点について検証したいと思います。


2024/02/06

HCL Nomad で GPS 座標の取得

Notes/Domino のクライアントソフトウェアには、ノーツクライアント以外に Nomad があります。現在 Nomad には、スマホやタブレット用の Nomad Mobile と WEB ブラウザで動作する Nomad WEB がありますが、今回は手持ちのデバイスで確認できる Nomad Mobile に関してです。

スマホやタブレットには、さまざまなセンサー類が付いていますが、Nomad では GPS にアクセスする機能が Notes 11 から追加されました。リリース当時、検証はしたのですが、先日久しぶりに開発しようとしたらすぐに思い出せず、ヘルプのお世話になりました。せっかくですからここでまとめておこうと思います。


GPS アクセスの概要

GPS にアクセスするためには Notes 11 で LotusScript に追加された次のクラスを使用します。

  • NotesGPS
  • NotesGPSPosition
  • NotesGPSCoordinates

3 つもあるのでオブジェクトを作成して、座標を Get するだけ、というように単純な使い方ではありません。GPS と言えばおおむね現在の位置を取得する目的に使用しますので、その機能だけを提供する関数を作成しておきましょう。


現在位置を取得する関数

GPS の座標は緯度(Latitude)と経度(Longitude)の 2 つの数値で示されます。デバイスの GPS にアクセスして現在位置の座標を返す GetGPSPos_Cur という関数を作成してみました(新規のスクリプトライブラリ lsGPS に作成)。

取得に成功した場合、戻り値が True となり、引数に座標をセットして返します。

Option Declare

Public Function GetGPSPos_Cur(rdLatitude As Double, rdLongitude As Double) As Boolean
   Dim ns As New NotesSession
   Dim gps As NotesGPS
   Dim gpsp As NotesGPSPosition
   Dim gpsc As NotesGPSCoordinates

   On Error GoTo ErrProc

   Set gps = ns.CreateGPS()
   If gps.RequestAccess() Then
      Set gpsp = gps.GetCurrentPosition()
      Set gpsc = gpsp.Coordinates
      rdLatitude = gpsc.Latitude
      rdLongitude = gpsc.Longitude

       GetGPSPos_Cur = True
   End If

ExitProc:
   Exit Function

ErrProc:
    GetGPSPos_Cur = False
   Resume ExitProc
End Function


座標取得までの流れは次の通りです。

  • NotesSession クラスの CreateGPS メソッドで NotesGPS クラスのオブジェクトを生成
  • RequestAccess メソッドで GPS に対するアクセスを確認
  • GetCurrentPosition メソッドで現在位置を NotesGPSPosition オブジェクトとして取得
  • Coordinates プロパティから座標を管理する NotesGPSCoordinates オブジェクトを取得
  • Latitude と Longitudeプロパティより座標を取得

デザイナーヘルプでは、Coordinates プロパティのように記載のない機能を使用していますが、これでコンパイルが通り、実行もできるので”よし”としましょう(12.0.2 で確認)。


なお、モバイルデバイスで Nomad に対して GPS のアクセスを禁止した場合や、GPS を持たない PC で実行した場合はエラーが発生します。そこで、エラーの場合は False を返すようにエラー処理を追加しています。


関数の組み込み

GPS から現在位置の座標を取得し、現在の文書にセットするエージェントを作成します。

Option Declare
Use "lsGPS"

Sub Initialize
   Dim ns As New NotesSession
   Dim nuiw As New NotesUIWorkspace
   Dim nuid As NotesUIDocument
   Dim nd As NotesDocument

   Dim dLat As Double
   Dim dLon As Double

   Set nuid = nuiw.Currentdocument
   Set nd = nuid.Document

   If GetGPSPos_Cur(dLat, dLon) Then
      nd.Latitude = dLat
      nd.Longitude = dLon
   Else
      MsgBox "GPS情報取得でエラーが発生しました。", 16
   End If
End Sub

トリガーは「エージェントリストの選択」、対象は「なし」で設定しています。


続いて、テストフォームを作成します。

緯度と経度を表示するフィールドと作成したエージェントを実行するアクションボタン[GPS情報取得]を作成します。


実行テスト

Nomad からアプリケーションを開き、テストします。[GPS情報取得]ボタンをタップすると現在位置の座標が表示されます(GPS に対する使用を許可した場合)。

GPS を持たない PC などで実行した場合は、エラーメッセージが表示されます。






2024/02/04

ダイアログリストフィールドの設定と挙動

前々回は @DbColmun、前回は @DbLookup の使い方をまとめました。使用したのはダイアログリストフィールドで、入力した部に従属する課だけを検索して表示する事例を紹介しました。ただ、このままの状態では、選択肢が正常に表示されない現象が発生します。その原因と対策について整理します。


問題となる現象

まずは、症状の確認です。

部を選択して課を選択しようとすると正しく部に従属したかが表示されます。そこで、いったん選択画面をキャンセルして、部を変更します。再度、課を選択しようとすると、選択肢が更新されず、変更前の選択肢がそのまま表示されます。

課を選択するフィールドには次の通り式を記述しました。"NoCache" を指定して毎回最新情報を取得する設定になっているのになぜでしょうか?

@DbLookup("Notes":"NoCache"; ""; "vFunc"; Dept; 2)


原因と対策

この症状の原因は @DbLookup 式が評価(実行される)タイミングにあります。

ダイアログリストフィールドの[制御]タブには「キーワードの変更時にフィールドを更新」と「文書の更新時に選択肢を更新」の2つのオプションがあります。似たような名称ですが、それぞれ役割があります。

まず、「キーワードの変更時にフィールドを更新」は、ダイアログリストの選択肢を変更したら画面を更新する設定になります。画面を更新すると計算結果フィールドの式や非表示式が評価され再計算されることになります。

「文書の更新時に選択肢を更新」は、画面を更新したタイミングで選択肢を更新する動作となり、「式で選択肢を設定」している場合の式を再評価(再実行)します。

今回の事例で言うと、部のフィールドでは「キーワードの変更時にフィールドを更新」、課のフィールドでは「文書の更新時に選択肢を更新」を指定します。

こうすることで、部を再選択すると画面が更新され、その更新により課の選択肢の式が再評価され、選択肢が再取得されるという流れになります。


まとめ

今回はダイアログフィールド利用の注意点として、「キーワードの変更時にフィールドを更新」と「文書の更新時に選択肢を更新」の2つのオプションの機能について紹介しました。設定しなくても初回は正常に動作してしまうので、発見しにくく、バグになりやすいので注意しましょう。

ところで、このオプションの名称 ”フィールドを更新” と言ったり "文書の更新" と言ったり用語が安定していませんよね。そして、”ダイアログリスト” フィールドは以前のバージョンでは、”キーワード” フィールドと呼んでいました。その名残がオプション名称に残っていますね。

このあたりを統一させてくれるだけでももう少しわかりやすくなると思うんですけどね...