2022/03/19

NotesQueryResultsProcessor で「最寄り」を得るには

HCL Nomad Mobile では、LotusScript を使用してスマートフォンのGPSセンサーから現在地の緯度経度などの情報を得ることができます。→参考

以前からこの機能を何かノーツで活用できるものは無いかと思案していました。

例えば「都内駐車場」というノーツアプリケーションがあったとします。ここに保存されている緯度経度と現在地の緯度経度から距離を算出して最寄りの駐車場を返す機能を考えてみます。

現在地の緯度経度の値は変化するので、現在地からの距離も当然のことながら変わります。

現在の時刻や現在地の緯度経度といったような「都度変化する値」をビューの選択式や列式へ埋め込んでおくことは、ビュー索引のメンテナンスのコストを考えると「通常」やりません。機能を呼び出すたびにすべての文書(またはビューエントリ)を読み込んで現在地の緯度経度の値とともに距離を求める式に値をあてはめて計算する、といった手法をとるように思います。このときすべての文書(またはビューエントリ)の数が少なければ全件読み込んでも構いませんが、都内駐車場の数のように万を超えてくると話はかわります。

さらに距離が最も近いものを返すには、算出した値のソートも必要です。ビューでソートするには算出した距離を列にセットする必要があります。


さて、Notes/Domino バージョン 12 の LotusScript と Java に、NotesQueryResultsProcessor クラス(長いので以降 "QRP" と呼びます)が加わりました。

QRP はアプリケーションから取得した文書セットを計算・ソートし、その結果をビューや JSON へ出力することが可能です。

JSON出力ならばビューのメンテナンスコストの心配は不要ですし、仮にビューへ出力したとしても、そのビューは短期間(デフォルトで24時間)で削除される仕様のため、「通常」ならやらない「都度変化する値」も扱いやすいのが特徴のひとつと思います。

2022年3月のテクてくLotus技術者夜会で QRP を紹介したときのスライドを共有します。


それから、今回は都内駐車場ではなく県庁所在地ですが、最寄りの所在地を返す LotusScript のサンプルを共有します。

このコードがあるアプリケーションには都道府県ごとの文書(データ)があり、それぞれの文書には県庁所在地の緯度と経度が lat と lng というフィールド名で保存されています。コードの Initialize にある getNearestTopX の引数に現在地の緯度と経度(コードでは固定値をセットしています)を渡すと、現在地からの距離が最も近い県庁所在地から順に配列要素の数だけ返します。

> tell amgr run "latlngdata.nsf" 'NearestTop3'
[2760:0009-8360] 2022/03/20 09:39:58   AMgr: Start executing agent 'NearestTop3' in 'latlngdata.nsf'
[2760:0009-8360] 2022/03/20 09:39:59   ビューの再構築 - コンテナまたは索引がありません。 (読み込み C:\IBM\Domino\data\latlngdata.nsf ビュー 文書 '(distance)')
[2760:0009-8360] 2022/03/20 09:39:59   Agent Manager: Agent printing: No1: 栃木県
[2760:0009-8360] 2022/03/20 09:39:59   Agent Manager: Agent printing: No2: 埼玉県
[2760:0009-8360] 2022/03/20 09:39:59   Agent Manager: Agent printing: No3: 東京都
[2760:0009-8360] 2022/03/20 09:39:59   AMgr: Agent 'NearestTop3' in 'latlngdata.nsf' completed execution

サンプルには JSON とビューの2つのパターンを用意しました。上のコンソールコマンドはビューのほうを実行したときの結果ですが、下に示すサンプルでは getTopXFromView の呼び出しをコメントアウトしてますので、必要に応じて getTopXFromJson のほうをコメントアウトして getTopXFromView の呼び出しを有効にしてください。

qrp の ExecuteToなんとかメソッドのパフォーマンスはいずれ検証したいとは思っていますが現状では未確認です。ここでは qrp を使ったコードのサンプルのひとつとしてご覧いただければと思います。

Option Public
Option Declare
Sub Initialize
	Dim lat!, lng!
	Dim no1$, no2$, no3$
	Dim topX(1 To 3) As String
	Dim counter%

	lat! = 36.2929247 '緯度
	lng! = 139.6049848 '経度

        Call getNearestTopX( lat, lng, topX ) '緯度, 経度, 最寄り

	ForAll o In topX
		counter = counter + 1
		Print "No" & counter ": " & o
	End ForAll
End Sub
Sub getNearestTopX( lat!, lng!, topX As Variant )
	Dim ss As New NotesSession
	Dim db As NotesDatabase
	Dim dql As NotesDominoQuery
	Dim qrp As NOTESQUERYRESULTSPROCESSOR
	Dim col As NotesDocumentCollection
	Dim query$, formula$
	Const viewname$ = "distance"
	
	Set db = ss.Currentdatabase

	Set qrp = db.Createqueryresultsprocessor()
	Set dql = db.Createdominoquery()
	query = |@All|
	Set col = dql.Execute( query )
	Call qrp.Addcollection( col, "allDocs")

	'2地点間の距離を算出する式
	formula = |@Sqrt(@Power((| & CStr(lat!) & | - lat) / 0.0111; 2) + @Power((| & CStr(lng!) & | - lng) / 0.0091; 2))|

	Call qrp.Addcolumn( viewname, "", formula, SORT_ASCENDING, False, False ) '距離を昇順にソート
   	Call qrp.Addcolumn( "pref", "", "", SORT_UNORDERED, False, False ) '都道府県名
'Call getTopXFromView( db, viewname, qrp, topX ) '結果ビューを使用するパターン Call getTopXFromJson( qrp, topX ) 'JSONを使用するパターン End Sub Sub getTopXFromView( db As NotesDatabase, viewname$, qrp As NOTESQUERYRESULTSPROCESSOR, topX As Variant ) Dim view As NotesView Dim nav As NotesViewNavigator Dim ent As NotesViewEntry Dim i% Set view = db.Getview( "(" & viewname & ")" ) If Not view Is Nothing Then Call view.Remove() '結果ビューが残っていたら削除する Set view = qrp.Executetoview( viewname ) '結果ビューの生成、デフォルトでは24時間で削除される Set nav = view.Createviewnav() Set ent = nav.Getfirst() For i = 1 To UBound( topX ) topX( i ) = ent.Columnvalues( 1 ) '2列目(都道府県名) Set ent = nav.Getnext(ent) Next End Sub Sub getTopXFromJson( qrp As NOTESQUERYRESULTSPROCESSOR, topX As Variant ) Dim nav As NotesJSONNavigator Dim elm As NotesJSONElement Dim i% Dim pointer$ Set nav = qrp.ExecuteToJson() 'JSONナビゲータオブジェクトの生成 For i = 1 To UBound( topX ) pointer = |/StreamResults/| & i - 1 & |/pref| Set elm = nav.Getelementbypointer( pointer ) topX( i ) = elm.Value Next End Sub

0 件のコメント:

コメントを投稿