2025/03/22

@Word の活用

前回 紹介した @Word 関数の活用について考えます。

私が特に便利だと感じるのは、マスタデータの選択や検索をするシーンです。マスタデータは通常、名称だけではなく、コードや金額など付随する複数の情報を持ちます。アプリでマスタデータを選択する機能を作成する場合、名称と金額など、マスタ内の複数の項目を取得するのが一般的です。

@Word を使えば、このような機能を効率的に作成できます。


マスタデータ

まずは、単純な商品マスタをサンプルとして準備します。フォームは、商品コード、商品名、単価、カテゴリの 4 項目とします。

この文書を商品コードでソートしたビューを作成し、各項目を配置します。

ビュー名 商品マスタ01
ビュー別名vAppMst01


商品選択機能

続いて、アプリをイメージしたフォームに、商品選択機能を作成します。選択画面は @PickList で」実現し、選択した商品の 4 項目、すべての情報を取得する仕様とします。

この【商品選択】ボタンはどのような式を記述すればいいでしょうか?@関数を覚えたての方なら以下のように書くと思います。

xReturn := @PickList([Custom]:[Single]; @DbName; "vAppMst01"; "選択"; "商品を選択してください。"; 1) ;

@SetField("CD"; xReturn);
@SetField("Name"; @DbLookup("":"NoCache"; @DbName; "vAppMst01"; xReturn; "Name"));
@SetField("Category"; @DbLookup("":"NoCache"; @DbName; "vAppMst01"; xReturn; "Category"));
@SetField("Price"; @DbLookup("":"NoCache"; @DbName; "vAppMst01"; xReturn; "Price"))

まず、@PickList の戻り値で、商品コードを取得します。それ以外の項目は、@DbLookup で再検索し取得します。


@Word でアクセスを削減

上記の式でも正しく動作します。ですが、@PickList とそのあとの再検索で、同じビューに 4 回もアクセスしていて、非効率です。この機能は @Word を使えば効率的に記述できます。

まず、ビューを細工します。最後に列を追加して、次のような式を記述します。

xDelimiter := "///";
CD + xDelimiter + Name + xDelimiter + @Text(Price) + xDelimiter + Category

この式で、マスタのすべての項目が特定の区切り文字で繋げられ、1 列の中に表示されます。この列を取得すれば、必要な項目をまとめて取得できるという算段ですね。なお、この列は、選択画面には不要ですので非表示にしておきます。

※ 記事内の図や式では別のビュー『商品マスタ02 | vAppMst02』として記述しています。


続いて、【商品選択】ボタンの式を以下のように変更します。

xDelimiter := "///";

xReturn := @PickList([Custom]:[Single]; @DbName; "vAppMst02"; "選択"; "商品を選択してください。"; 6) ;

@SetField("CD"; @Word(xReturn; xDelimiter; 1));
@SetField("Name"; @Word(xReturn; xDelimiter; 2));
@SetField("Price"; @TextToNumber(@Word(xReturn; xDelimiter; 3)));
@SetField("Category"; @Word(xReturn; xDelimiter; 4))

この時活躍するのが @Word です。区切り文字で分割し、部分文字列を取得する機能がぴったりハマります。部分文字列の 1 つ目は商品コード、2 つ目は商品名、... というように順に読み込んで、フィールドに当てはめるだけです。

ポイントは、各項目に含まれない文字列を区切り文字に設定し、ビューとボタンで同じものを設定することです。区切り文字は、複数文字を指定できるので、安心ですね。

この方法であればビューの接続は 1 度だけになります。


複数値の場合は特に効果的

ノーツでは、複数値の区切り文字を改行にして、疑似的な表のように見せる技をよく使います。


この場合、【商品選択】ボタンの @PickList は [Single] キーワードを外して、複数文書選択できるようにします。

xReturn := @PickList([Custom]:[Single]; @DbName; "vAppMst02"; "選択"; "商品を選択してください。"; 6) ;

@SetField("CD"; @Word(xReturn; xDelimiter; 1));
@SetField("Name"; @Word(xReturn; xDelimiter; 2));
@SetField("Price"; @TextToNumber(@Word(xReturn; xDelimiter; 3)));
@SetField("Category"; @Word(xReturn; xDelimiter; 4))

この対応だけで、複数の商品を選択する機能が実現できます。

@PickList で複数文書を選択すると、結果はリスト値で返されます。そして、@Word は、リスト値に対応していますので、そのままのコードで都合よく処理してくれます。


まとめ

ノーツのビューは、文書数が多い、更新頻度が高いなどの理由により、レスポンスが遅くなることがあります。今回の事例のように、ビューに接続する回数を減らせば、比較的に良いレスポンスを維持することができます。

また、@Word を組み合わせれば効率的に必要なデータにアクセスでき、プログラムをシンプルに保つことができます。今回は、@PickList を例にしましたが、@DbLookup でも利用できる技です。いろいろな場面で活用できますので、是非とも覚えてください。


2025/03/19

@Word の使い方

今回は @Word 関数を紹介します。文字列から部分文字列を取得する関数で、便利に使えるケースがある関数です。ぜひ覚えておきましょう。

ヘルプでは @Word は文字列から指定された ”単語” を返すと説明されています。ここでいう ”単語” とは文字列を指定した区切り文字で分割した一つを指すので、”単語” より ”部分文字列” のほうが誤解がないと思います。


構文

ヘルプによると @Word の文法は次の通りです。

@Word( string ; separator ; number )

引数の役割は次の通りで、どの引数も省略することはできません。

1 string 文字列
文字列リスト
検索され取得元となる文字列
2 separator 文字列 区切り文字
3 number 数値 取り出す位置
正数は前から、負数は後ろから

LotusScript では、StrToken 関数が同等の機能を提供します。


戻り値

@Word の戻り値は次の通りです。

文字列
文字列リスト
string 内の number で指定した位置にある部分文字列を返します


サンプル

単純なフォームを作成してテストします。各引数を入力できるようにし、その値を使用して @Word を実行します。

プリビューして、ソース文字列に "Returns the specified word from a text string."、区切り文字に " " (スペース)を入力します。

取り出し位置に 3 を指定すると "specified"、-2 を指定すると "text" が返されます。なるほど、Word という関数名になった理由がよくわかりますね。日本語で ”単語” の抽出には利用できないですが...

ちなみに、存在しない数値、例えば上記例で 10 を指定すると、空の文字列が返され、エラーとはなりません。


また、この関数は構文や戻り値に記載した通り、リスト値に対応しています。次の例では氏名から 姓 だけを取得しています。

次の事例では、氏名と所属を ”/” 区切りで表現していたとします。最後が事業所で、後ろから二つ目が部門(部)だったとします。以下のように記述すれは簡単に部門(部)が取得できます。


まとめ

上記の例において、一般に所属は役職や部門によって階層が異なります。このようなデータでは、負数を指定して後ろから取得する機能が有効です。

このように @Word は特定の条件がある場合、とても便利に機能します。

活用できるケースは多岐にわたるので、探してみましょう。一発で取得できる事例に出会うとコードも気分もスッキリしますよ。


2025/03/14

3 年目突入 !!

2023 年に始めたブログ『出直し!! ドミノ塾 』は、本日 3 月 14 日で 2 周年を迎え、3 年目に突入しました!


人気の記事

2 年分をざっと振り返ると、投稿した記事は 約 330 本、約 9 割の記事が開発系でした。中でも @関数や LotusScript など、90 年代後半のノーツバブルのころからある機能が多数を占めています。プロフィールに書いていますが『オールドタイプ』全開ですね(笑)

記事もそれなりにたまってきたので、直近 1 年分のアクセスログを調査してみました。アクセス頻度の上位 20 の記事は下表の通りでした。

結果から見て取れるのは、

  1. @関数の使い方
  2. リッチテキストの操作
  3. Excel 連携

が主で、それに続いて LotusScript といったところでしょうか...。やはり、すそ野が広い分、基礎的なコンテンツにはアクセスが集まるのですね。

逆に DXL(DXL Step-by-Step) や WebAPI連携(つないでみよう)の連載は、マイノリティ向けの記事と思っているのですが、それでもランクインするということは、一部の方には気に入っていただけているのだと思います。

今後も硬軟織り交ぜながらバランスよくネタを選んでいこうと感じました。

◇ Top 20

1 @DbLookup の使い方
2 Notes - Excel 連携:#35)Excel で使用する単位と変換
3 Notes - Excel 連携:#4)ノーツ文書をワークシートに出力
4 リッチテキスト:#16)テキストの抽出
5 リッチテキスト:#15)添付ファイルのダウンロード
6 @Prompt の使い方
7 LotusScript(ラベルのクリック)
8 リッチテキスト:#1)フィールドの作成
9 NSF メンテナンス 三種の神器
10 DXL Step-by-Step(連載の目次ページ)
11 Notes - Excel 連携:#6)マクロの記録の活用 と 文法の違い
12 リッチテキストの基本操作(連載の目次ページ)
13 つないでみよう(連載の目次ページ)
14 @関数編(出直しヘルプ「@関数編」の目次ページ)
15 @DbColmun の使い方
16 リッチテキスト:#14)既存リッチテキストフィールドの取得
17 つないでみよう:#14)GPT4o で画像認識 - 準備作業
18 リッチテキスト:#6)文字色の設定
19 リッチテキスト:#3)添付ファイルの作成
20 Notes - Excel 連携(連載の目次ページ)


まとめ

この 2 年間を振り返ってみると、ずっと Notes/Domino を検証しながら、キーボードをたたいて記事を書いていたような気がします...。連載 100 回記念 で記載しましたが、ノーツ担当 30 周年記念に加えて、私の社会人人生をまるっとノーツにお世話になったお礼を兼ねて始めたこのブログですが、それなりに恩返しはできたかなと思っております。

こんな風に書くと終了するのか?と思われるかもしれませんが、まだもうしばらく続けるつもりです。ただ、ネタが尽きてきたので投稿頻度は減ってくると思います...。

あらためまして、3 年目もどうぞよろしくお願いいたします!


2025/03/11

DXL Step-by-Step:#50)ノード操作 ③ - NotesDOMNode の使い方

DXL を自由自在に操作するためには NotesDOMNode の役割と使い方を理解することが重要です。


NotesDOMNode クラスとは

まずは、デザイナーヘルプで NotesDOMNode を確認します。

クラスの説明には、

文書ツリーの単体のノードを表します。

とだけ記載されています。

また、『作成方法とアクセス方法』には、以下の通り記載されています。

NotesDOMNode クラスは抽象クラスであるため、NotesDOMNode オブジェクトを作成できません。代わりに、NotesDOMDocumentNode クラスの適切な Create メソッドを使用して、特定の派生ノードクラスのオブジェクトを作成します。
NotesDOMNode クラスとその派生クラスのオブジェクトは、作成元の NotesDOMParser オブジェクトとの関連性を保持します。このメソッドは該当する 2 つのノードが同じ DOM パーサーから派生した場合のみ機能します。

これだけでは、さっぱりわかりませんね...。ポイントは、以下の部分です。

派生クラスというセクションがあり、NotesDOMElementNode などのクラスがリストアップされており、最後に「NotesDOMNode クラスから継承」とあります。

簡単に説明すると、NotesDOMElementNode などのクラスは、

  • NotesDOMNode を基に定義されたクラスであること
  • NotesDOMNode は派生クラスの値を持てること

を表します。


言葉だけではわかりにくいので具体的なコードとともに確認しましょう。

NotesDOMElementNode には、配下のノードを検索するメソッド GetElementsByTagName が存在します。この命令を使用して、"table" ノードを検索し、順に取得する処理を例にします。

   '表を検索し順に処理
   Dim dnl As NotesDOMNodeList
   Dim dn As NotesDOMNode
   Dim i As Integer

   Set dnl = denForm.GetElementsByTagName("table")
   For i = 1 To dnl.NumberOfEntries
      Set dn = dnl.GetItem(i)

      '”表”に対する処理を記述
         ・・・
   Next

GetElementsByTagName メソッドの戻り値は NotesDOMNodeList です。このクラスは、検索結果を保持するためのクラスで、複数の NotesDOMNode をコレクションのように保持します。上記サンプルでは、GetItem メソッドで、検索結果から 1 つずつ取得して順に処理させていますが、戻り値の型は NotesDOMNode となっています。実体は ”表” を表すノードですから、NotesDOMElementNode となります。

このように NotesDOMNode クラスは、派生クラスに記載されているオブジェクトを保持できる汎用的なクラスということになります。


NotesDOMNode クラスの使い方

続いては、上記で取得した NotesDOMNode の使い方です。

前述の通り、中身は NotesDOMElementNode クラスのオブジェクトです。ですので、下記の通り NotesDOMElementNode の変数 den に代入できます。代入後の den を利用すれば NotesDOMElementNode クラスのプロパティやメソッドにアクセスできます。

   '表を検索し順に処理
   Dim dnl As NotesDOMNodeList
   Dim den As NotesDOMElementNode
   Dim dn As NotesDOMNode
   Dim i As Integer

   Set dnl = denForm.GetElementsByTagName("table")
   For i = 1 To dnl.NumberOfEntries
      Set dn = dnl.GetItem(i)

      '”表”に対する処理を記述
      Set den = dn
      'den を使えば NotesDOMElementNode の機能が使える
          ・・・
   Next

NotesDOMNode の実態は、NotesDOMElementNode だけでなく、派生クラスとして記述されているさまざまなオブジェクトを取りうることになります。上記例では、"table" ノードを検索したので、実態は NotesDOMElementNode だとわかります。ですが、前回 紹介した FirstChildNextSibling で取得したノードの場合はどうでしょう。DXL 内で固定の文字列を表す NotesDOMTextNode の可能性もあります。

取得したノードの種類を調べるには、NodeType というプロパティを使用します。戻り値は数値で、多数のノードが定義されています。ただ、一般的な DXL のコーディングでは 1 と 3 を覚えておけば十分でしょう。

定数 クラス名
1 DOMNODETYPE_ELEMENT_NODE  NotesDOMElementNode
3 DOMNODETYPE_TEXT_NODE  NotesDOMTextNode
9 DOMNODETYPE_DOCUMENT_NODE  NotesDOMDocumentNode

9 の NotesDOMDocumentNode については 前々回 で必須クラスとして記載したので掲載していますが、NotesDOMDocumentNode であるかを判断することはないと思います。


NodeType は、具体的には次のように利用します。

FirstChild プロパティで取得したノードを NotesDOMNode の変数でいったん受け取ります。その NodeType を確認したうえで、オブジェクトの型に合致した変数に代入します。

   Dim dn As NotesDOMNode
   Dim dnChild As NotesDOMNode
   Dim den As NotesDOMElementNode
   Dim dtn As NotesDOMTextNode
          ・・・
   Set dnChild = dn.FirstChild
   If dnChild.NodeType = DOMNODETYPE_TEXT_NODE Then
      'テキストノード
      Set dtn = dnChild
          ・・・
   ElseIf dnChild.NodeType = DOMNODETYPE_ELEMENT_NODE Then
      'エレメントノード
      Set den = dnChild
          ・・・
   End If


まとめ

今回は NotesDOMNode クラスの使い方についてまとめました。少々ややこしいのですが、ここをしっかり押さえておくと DXL のプログラミングが格段に速くなると思います。

ところで、この記事をここまで読んで、あれっ?? と思うことはなかったですか?

NotesDOMNode の派生クラスの一覧(最初の画像内)に NotesDOMTextNode が含まれていません。ですが、上記サンプルプログラムでは、代入していました。これは、ヘルプやサンプルコードの間違いではありません。

      'テキストノード
      Set dtn = dnChild
          ・・・

NotesDOMTextNode のヘルプを見ると継承元が NotesDOMCharacterDataNode となっています。そしてそのクラスの継承元が NotesDOMNode となっています。カスケードした関係だから、記載がなかったということですね。

 NotesDOMNode
 ┗ NotesDOMCharacterDataNode
   ┗ NotesDOMTextNode 

 

前回 DXL Step-by-Step


2025/03/09

Fix の適用ってお得 !? ドメイン検索の索引作成時間が改善!

今回も昨年末の Domino サーバのメールルーティングトラブル に関連する件です。トラブルの緊急対応だったので、半ば強制的に最新の Fix を適用しました。その作業をぼやくのではなく、”うれしい誤算” があったお話です。


棚ボタな現象

社内ではドメイン検索を運用しています。運用スケジュールとして、毎週土曜日に全対象 DB の索引を再作成する作業を行っています。対象 DB の数や文書数が多く、かなりのサイズを索引するため、実行時間が非常に長くなります。この処理時間が、最新 Fix 適用でずいぶん短くなったのです!

バージョン 処理時間
11.0.1 FP2 約 15 時間
11.0.1 FP9 約 8 時間

最新 Fix 適用前後で対象の DB の変更は行っていませんので、Fix 適用の効果といえると思います(もちろんサーバ機のリソースなども変更していません)。

少し改善するだけならまだ理解できるのですが、ほぼ半減となるとさすがに障害を疑いますよね。カスタマーサポートに問い合わせしたところ、最新 Fix にはさまざまなアップデートがあり、それに起因して短縮したとの返答でした。

運用上、問題が発生していないこともあり、この返答に納得しました。まさに、うれしい誤算、棚からぼた餅、瓢箪から駒ですね !!


修正リスト

サポートからいただいた修正リストをまとめると次の通りです。項目数が多く驚きましたが、日々改善されているのはありがたいですね。

バージョン SPR# 修正
11.0.1 FP3 JBUDBWYRRN 添付ファイルの全文索引作成時にファイルタイプの判定を改善しました。 
YCHABULJCB Domino 10.x.x で作成された索引ファイルのサイズが Domino 9.0.1 で作成されるものよりも大きくなる問題を修正しました。このリグレッションは 10.0 で確認されました。
11.0.1 FP4 JBUDBNFH6R データベースの全文索引作成時のオプションで添付ファイルの索引に簡易検索を選択している場合、添付ファイルのファイル名を検索することができなくなる問題を修正しました。このリグレッションは 11.0.1 で確認されました。
JBUDC36HZX 大量の添付ファイルを含むデータベースに対して全文索引の作成や更新を行うとパフォーマンスが悪化する問題を修正しました。
JBUDC57T3N 全文索引の添付ファイルをフィルタリングする際に、bmp ファイルが無視されない問題を修正しました。
NNAIBZV2VR 全文索引が作成されているデータベースで、新規文書の全文索引作成が停止することがあるという問題を修正しました。
RRENBBRJN4 索引の更新が先に実施されることで検索が顕著に遅延する、全文索引の自動更新の機能について修正しました。このリグレッションは 10.0.1 で確認されました。
11.0.1 FP5 WSZO7JU6U6 文書の全文索引作成時に、ログにエラー 3351 (Open error) が表示される問題を修正しました。
11.0.1 FP7 MOBNCE5N54 全文索引でエラー番号 3351 が発生すると、全文索引の再構築が必要になる問題を修正しました。
MOBNCGWPWM GTR のエラー番号 223 が発生すると、稀に全文索引の再構築が破損する問題を修正しました。
MOBNCJETFX 全文索引処理で暗号化した MIME 文書の索引を作成する際に「Handle out of range」エラーが発生し Domino サーバーがクラッシュしてしまう問題を修正しました。
NNAICLEE5H ドメイン検索で、Brute Forceを使用して索引作成された添付ファイルを見つけることができない問題を修正しました。このリグレッションは 12.0.2 で確認されました。
11.0.1 FP8 DCONBYWHG9 サイズの大きな動画ファイルに対して全文索引を作成する際に、動画ファイルが「MediaDataBox」から始まるファイル名で Domino の一時ファイルフォルダ内に残ってしまう問題を修正しました。
JBUDC36HZX 大量のファイルが添付されているデータベースに対する全文索引作成時のパフォーマンスに関する問題を修正しました。
JBUDC57T3N 全文索引の添付フィルタリング時に bmp ファイルが無視される問題を修正しました。
WSZO7JU6U6 文書の全文索引作成時に、ログにエラー 3351 (Open error) が表示される問題を修正しました。
11.0.1 FP9 MOBNCRUJSJ 全文索引作成時に、クラッシュの原因となる文書が隔離されないことがある問題を修正しました。

ところで、”リグレッション” って単語がたびたび出てきます。少々、気にはなったのですが、今回は ”うれしい誤算” の話なので黙殺したいと思います...。


2025/03/07

Domino インストールエラーの対処

年の瀬に Domino サーバのメールルーティングトラブル が発生しました。すべての Domino サーバで発生するトラブルで、影響は甚大です。Notes/Domino のトラブルでこれほど大きいのは珍しいですね。

発災からほぼ 3 ヶ月経ちましたので、対応も進み状況は落ち着いたのではないでしょうか?

私が管理するサーバも影響を受けたのですが、メーカーによる迅速なパッチ提供があり、それを適用することで回避できました。対応としては、最新の FixPack の適用とこのトラブルの Interim Fix を適用を行いました。

ただ、その作業でインストールトラブルに見舞われました。他の HotFix 適用など将来も起こりうる現象なので、後学のためにまとめておきます。


なお、本件の解決は、HCL Software の Customer Support に協力をいただきました。超ド級トラブルの最中だったので、サポート担当に皆さんは、私ら以上にパニック状態だったと思います。それでも、迅速かついつも通りの丁寧な対応をくださいました。解決できてありがたいのはもちろんですが、それ以上に感心いたしました。IT 担当者として見習わなくては...。


トラブル①:インストール中のエラー

まずは、FixPack インストール途中で発生したエラーです。

インストールを開始してしばらくたってから「RenameTempFiles: ERROR: Can't rename・・・」のエラーが発生し、インストールに失敗するというものです。

このエラーについては、以下の技術情報がありました。この記事によると Windows Management Instrumentation サービスが実行していることが原因だそうです。

Domino サーバーの Fix Pack が "RenameTempFiles: ERROR: Can't rename old <file name>: access problem" のエラーでインストールできない


トラブル②:インストーラが起動しない

インストールのトラブルはもう一つあり、FixPack のインストーラを実行したら  "Notes/Domino or a Notes/Domino related process is still running." とメッセージが表示され、インストーラが起動しないというものでした。

この問題もすでに技術情報がありました。

Fix Pack または Hotfix が "Notes/Domino or a Notes/Domino related process is still running." エラーでインストール/アンインストールできない

エラー発生の原因として、以下の 6 点が挙げられていますが、3 に起因して発生しています。またしても Windows Management Instrumentation サービスが原因だったのです。

  1. Domino サーバー関連のプロセス(nsd.exe も含めて主に "n" で始まるプロセス、scontroller.exe、jconsole.exe)およびサービスが終了していない。
  2. バックアップソフト、もしくはウィルススキャンソフトが Domino サーバーのファイルにアクセスしている。
  3. Windows Management Instrumentation サービス (WmiPrvSE.exe) が稼動している。
  4. Fix Pack もしくは Hotfix のインストーラーを管理者権限で実行していない。
  5. Fix Pack もしくは Hotfix のインストーラーが OS のプロパティにて「読み取り専用」にチェックされている。
  6. Windows 側(OS 側)で、インストール先のフォルダに書き込み禁止権限が設定されている。


インストール手順

先の技術情報では『Windows Management Instrumentation サービスを停止する』とだけしか書かれていませんが、停止するだけではエラーが継続する現象がありました。これについては OS を再起動することで回避できました。

この経験を踏まえ、FixPack や Interim Fix の適用手順を改めて整理します。


1.Dominoサーバーサービスを無効に設定

サービスの一覧で Notes/Domino に関するすべてのサービスを無効化して、自動的に起動しないように設定します。

2.システムを OS から再起動 

Windows OS を再起動します。1 の設定により Notes/Domino 関連のサービスは起動しない。

3.画面上で起動している、すべてのアプリケーションを停止

4.Windows Management Instrumentation サービス の停止

サービスの一覧からサービスを停止します。その際、関連するサービスの停止を確認するメッセージが表示されますので、停止させます。停止するサービスは、1 つの場合と 2 つの場合がありました。

5.一時ファイルをすべて削除

コマンドプロンプトから set コマンドを実行し、TMP および TEMP 環境変数の示すディレクトリを確認、その一時ディレクトリを空にします。

6. その後 FP や IF をインストール

インストーラは、管理者権限で実行します。


まとめ

今回は、FixPack のインストール時に発生した Windows Management Instrumentation サービスによるエラーについて記述しました。

今回のトラブルは緊急対応だったため、準備もなく対応に当たりました。FixPack のインストールだけだったので、作業としては安易に考えており、業務時間中にサーバを停止してのぶっつけ本番作業としました。インストール途中でエラーが発生した時には、原因調査と並行して、ユーザを待たせないよう Domino サーバを起動することとなり、「中途半端な状態で、正常に起動するのか?」と 肝を冷やしました...。

このようなことにならないよう日頃から、手順を明確にしておくべきと再認識した次第です。サーバ管理者として、まだまだ半人前ってことですね...。

ちなみに、インストールエラーでキャンセルした後に起動した Domino サーバは、作業前の FixPack で無事稼働しました。インストーラってよくできているんですね。


2025/03/05

@Left, @Right, @LeftBack, @RightBack の違い

今回は文字列操作の中でもっとも使うといっても過言ではない @Left、@Right をまとめます。また、混乱してついついヘルプを見てしまう @LeftBack、@RightBack も併記して整理したいと思います。


基本的な構文

どの関数も同じ構文なので @Left を例に確認します。

@Left(stringToSearch; subString)

@Left(stringToSearch; numberOfChars)

これら関数の構文には 2 種類あり、2 つ目の引数が数値の場合と文字列の場合があります。引数の役割は次の通りで、どちらも省略することはできません。

1 stringToSearch 文字列
文字列リスト
検索され取得元となる文字列
2 subString 文字列 検索文字列
numberOfChars 数値 文字数

戻り値は、取得元となる文字列から抽出した部分文字列となります。


引数が文字列の場合

まずは、第 2 引数が検索文字列を指定するパターンです。

検索される文字列は "ABCXYZABC" で固定し、@関数と検索文字列 Chr を変化させながら検証します(例は @Left の場合)。

@Left("ABCXYZABC"; Chr)

検索文字列は、ヒットする文字以外に、イレギュラーパターンとして null と含まれない文字の場合を試しました。

Chr @Left @Right @LeftBack @RightBack
(null) (null) (null) (null) (null)
A (null) BCXYZABCABCXYZ BC
C AB XYZABC ABCXYZAB (null)
X
ABC YZABC ABC YZABC
0 (null) (null) (null) (null)

まずは、ヒットする C の場合です。@Left では左から検索し、ヒットした文字の左側である AB を返しています。@LeftBack では右側から検索するので、いきなりヒットすることになり、その左側を返しています。この構文では検索する方向が重要だといえますね。

イレギュラーパターンの場合は、すべて null となるので、わかりやすい結果となっています。

この動作を表にまとめると以下の通りとなります。


検索方法 取得する文字列
@Left 左 → 右 左側
@Right 右 → 左 右側
@LeftBack 右 → 左 左側
@RightBack 左 → 右 右側

@Left では、左から検索し、ヒットしたらその左側を返します。@Right の場合は右から検索し、右側を取得します。Left なら左、Right なら右でそろっているのでわかりやすいですね。

Back がつくと検索方向が逆になるだけで、取得する文字列の方向は変わりません。


引数が数値の場合

続いては、第 2 引数が文字数を指定するパターンです。文字列 "ABC" に対して、@関数と文字数 n を変えながら結果を確認します(例は @Left の場合)。

@Left("ABC"; n)

テストですので、イレギュラーな値についても検証しています。

n @Left @Right @LeftBack @RightBack
-1 ABC ABC (null) (null)
0 (null) (null)ABC ABC
1 A C AB BC
2
AB BC A C
3 ABC ABC (null) (null)
4 ABC ABC (null) (null)

まずは、妥当な文字数といえる n = 0 ~ 3 を例に挙動を確認します。

Back のついていない関数では指定した文字数を取得しています。@Left なら左、@Right なら右から、指定した文字数を取得するので、わかりやすい結果です。

Back がついている関数では、元の文字数 - n の文字数となっています。例えば、@LeftBack の場合、逆方向(Left の場合は右)から n 文字分を省いた文字列が返されます。

この動作を表にまとめると以下の通りとなります。


取得する文字列 取得方法
@Left 左側 左 から 文字数分を 取得
@Right 右側 右 から 取得
@LeftBack 左側 右 から 省く
@RightBack 右側 左 から 省く

関数名に Back がつく場合、逆方向から指定した文字数を ”省く” 動作となります。引数に指定した数値は "省く文字数" となり "取得する文字数ではない" ことが混乱のもとかもしれませんね。

n = -1 の場合は n = 0 と同じになるかと想定したのですが、予想外の結果でした。このような値をわざと指定する人はいないと思いますが、注意が必要かもしれません。


まとめ

今回は @Left、@Right、@LeftBack、@RightBack 関数の違いについてまとめました。引数のデータ型と機能の違いを覚えておきましょう。


2025/02/27

DXL Step-by-Step:#49)ノード操作 ② - ノード間の関係と取得方法

具体的なノード操作のテクニックを紹介する前にノード間の関係や種類、取得方法についてまとめます。過去の記事のおさらい的な部分もありますが、改めて整理します。


Domino オブジェクトと DXL の関係

DXL を操作するためには、DXL の基本構造を知る必要があります。

NotesDOMParer クラスは Domino オブジェクトを DXL に変換し、整形してくれるクラスです。このクラスの Document プロパティで ”DXL 全体” のノードである NotesDOMDocumentNode のオブジェクトを取得できます。そして DocumentElement プロパティにより ”Domino オブジェクト全体” のノードである NotesDOMElementNode オブジェクトが取得できます。

文書の場合、”文書全体” のノード名は document ですが、エージェントの場合は agent、イメージリソースでは imageresource でした(#6, #7, #8 を参照)。DocumentElement プロパティで取得するからと言って、ノード名は必ずしも document ではないので注意しましょう。


NotesDOMDocumentNode の役割

”DXL 全体” のノードである NotesDOMDocumentNode のオブジェクトには、特別な役割があります。それは、ノードの新規作成ができることです。

NotesDOMElementNode など NotesDOM???Node オブジェクトは多数ありますが NotesDOMDocumentNode 以外ではノードの作成ができません。


DXL の構造とノードの種類

文書内のノードのを確認すると次のようになっています。一般的に使用するのは 3 種類です。

NotesDOMElementNode は item や richtext などのノード名を持ち、配下に別のノードを保持することがあります。また、name や size などの属性を持つことがあります。属性には NotesDOMAttributeNode というクラスが割り当てらていますが、NotesDOMElementNode のメソッドからアクセスできるので、前回紹介した必須クラスに入れていません。

もう一つ重要なノードとして、NotesDOMTextNode があります。DXL 内で文字列を表すノードで、NotesDOMElementNode の子ノードとして存在します。


NotesDOMElementNode は 1 つ以上の子ノードを持つことができます。子ノードは NotesDOMElementNode の場合もあれば、NotesDOMTextNode の場合もあり、混在することもあります。

なお、NotesDOMTextNode は子ノードを持つことはありません。


ノード間の関係と取得方法

上記の通り、ノードには親子関係があります。そして、複数の子ノードを持ちうるので、兄弟の関係も存在します。NotesDOM???Node クラスには、それぞれのノードにアクセスするためのプロパティが用意されています。

この関係を理解すれば、自由に DXL サーフができますね。


FirstChild 最初の子ノード
LastChild 最後の子ノード
ParentNode 親のノード
NextSibling 次の兄弟ノード
PreviousSibling 直前の兄弟ノード


前回 DXL Step-by-Step 次回


2025/02/25

DXL Step-by-Step:#48)ノード操作 ① - DXL 操作で必須のクラス

先日、DominoHub の今年のイベントが告知されました。

DominoHub は Notes/Domino の日本最大のコミュニティイベントで、今年も昨年同様、6 月に東京で開催予定となりました。詳細は以下のリンクを確認ください。

DominoHub 2025 Tokyo Notes/Domino日本最大のコミュニティイベント

日程やセッションの内容などについては準備ができ次第更新されます。お楽しみに!


昨年の DominoHub 2024 では、『 ”DXL” でリッチテキスト縦横無尽』と題して、DXL の基礎からテクニックまでを総合的に紹介させていただきました。今回からしばらくの間は、”ノード操作” を主題に、このセッションの中から DXL のノードを自由自在に操作するテクニックを再編集してご紹介します。


DXL で利用するクラス

まずは DXL 操作で利用するノーツクラスのおさらいです。

DXL に関連するクラスには、NotesDXL で始まるクラスと NotesDOM の 2 種類があります(下図の青枠)。NotesDXL??? クラスは DXL の入出力で利用し、文書 ⇔ DXL の変換を行います。そして、NotesDOM??? クラスがノード操作を行うクラスです。DOM は Domino Object Model の略と認識すれは理解しやすいですね。


ノード操作で必須のクラス

NotesDOM??? クラスが今回のテーマとなるのですが、20 近いクラスが定義されていて、複雑そうです(紫枠)。ただ、一般的な DXL 操作では、これらすべてのクラスを使用しません。私の経験では赤線で示した 5 クラスを理解すれば、DXL を自由自在に操作できます。


文書の操作と必須クラス

文書の操作には、新規作成と編集があります。それぞれの操作で利用する必須クラスをさらに分類することができます。

新規 編集 クラス 役割
NotesDOMDocumentNode 文書全体のノード
ノードの新規作成機能を持つ
NotesDOMElementNode ノード名や属性を持つ一般的なノード
NotesDOMTextNode 文書内のテキスト(文字列)
NotesDOMNodeList ノード検索の戻り値で使用
複数のノードをコレクションのように保持
NotesDOMNode NotesDOMNodeList から取得したノード
NotesDOMElementNode や NotesDOMTextNode などさまざまなノードオブジェクトを取りうる

文書の新規作成では、各ノードを DXL 構造に従って構築するだけなので、NotesDOMDocumentNode でノードを作成し、適切な場所に配置する作業の繰り返しとなります。文書の編集では、編集する個所を見つける必要があります。ノード検索 GetElementsByTagName メソッドを使用するので NotesDOMNodeList と NotesDOMNode も利用することになります。


前回 DXL Step-by-Step 次回


2025/02/19

ソートはソートにあらず!?

今回は、Notes/Domino と他のシステムとの間でデータ交換を行うプログラムで経験したトラブルのご紹介です。


システム間の連携仕様

別のシステム内にあるマスタデータをノーツフォームの選択肢として利用したいとの要望があり、マスタデータをテキストファイルでもらい、ノーツ DB 内に文書として保存することにしました。

日々の更新に対応するべく、毎日夜間にマスタデータ全件をファイルを出力してもらい、未明にノーツエージェントで DB に取り込むこととしました。データ件数が多いこともあり、エージェントの処理は、全件削除して全件取り込みするのではなく、差分更新としました。そこで、連携ファイルはソートして出力してほしいと依頼しました。

 

エージェントの仕様

まず、エージェントの処理用にマスタデータをソートしたビューを作成します。そのビューを利用して、次のようなマスタ更新するエージェントを作成しました。

  1. ファイルから最初のマスタデータを取得
  2. ビューから最初の文書を取得
  3. お互いのデータを比較し、以下の処理を行う
    1. 一致する場合、ファイルとビューの双方から次のデータを取得
    2. ノーツにない場合は、ノーツにマスタデータを登録し、ファイルの次のデータを取得
    3. ファイルにない場合は、マスタデータを削除し、ビューから次のデータを取得
  4. ファイルとビュー両方のデータが最後になるまで 3 に戻る

お互いソートされたデータ同士なので順に存在確認すれば、追加 か 削除、更新しないを効率よく判断できるという算段です。


発生した問題点

運用後、エージェントは一見正常に動作していました。ただ、よくよくチェックすると、ファイルに更新がなくても一定数のデータの追加と削除が発生しました。


その原因がソートだったのです。


連携ファイルは他のシステムでソートされ、ビューはノーツでソートされています。同じ ”ソート” でもデータの並び順が違っていたのです。


ノーツだけでも問題は起こる

このようにソートしてもデータの並び順が発生する現象はシステムが違うから発生するとは限りません。ノーツだけでも同様の症状を再現できます。

以前『LotusScript で値のソートを作成』の記事で紹介したソートする関数 xSort を利用したサンプルエージェントを作成します。関数 xSort は割愛(先のリンクを確認ください)。

Option Declare

Sub Initialize
   Dim ns As New NotesSession
   Dim ndb As NotesDatabase
   Dim nd As NotesDocument

   Dim s As String
   Dim vSrc As Variant
   Dim vSort As Variant
   Dim i As Integer

   s = "Test,Test1,Test_1,Test-1,Test 1,Test2,Test_2,Test-2,Test 2"
   vSrc = Split(s, ",")
   vSort = xSort(vSrc)

   '結果を1件ごとに文書に出力
   Set ndb = ns.CurrentDatabase
   For i = 0 To UBound(vSrc)
      Set nd = ndb.CreateDocument()
      nd.Form = "SortText"
      nd.Text = vSort(i)
      nd.SortNum = i

      Call nd.Save(True, False)
   Next
End Sub

実行するとソート順とその値が文書に保存されます。この文書を表示するビューを作成し、値でソートします。すると、LotusScript と同じソート順とはなりません。


続いて、フォームを使って @Sort をテストします。

Src と Sort の 2 つのフィールド(複数値)を持つフォームを作成します。Sort フィールドは計算結果フィールドに設定し、Src フィールドに入力された値をソートする計算式を設定します。

プリビューして先の LotusScript のテストと同じ値を入力し、計算させます。結果を確認すると LotusScript、ビューのソートのどちらとも一致しません。


まとめ

改めて、LotusScript、ビュー、@Sort の値を比較すると次のようになります。なかなかバラバラな結果となっていますね...

LotusScript ビュー @Sort
Test
Test 1
Test 2
Test_1
Test_2
Test-1
Test-2
Test1
Test2
Test
Test-1
Test-2
Test 1
Test 2
Test1
Test2
Test_1
Test_2
Test
Test 1
Test 2
Test1
Test2
Test-1
Test-2
Test_1
Test_2

Notes の場合にはデータがソートされているかだけでなく、どうやってソートしたのかも重要なようです。特に他のシステムと連携する場合には要注意ですね。

もちろん、このような症状は、数字だけや英字だけの場合には発生しません。今回の事例のように記号やスペースを含む場合に注意が必要です。