全文検索の結果を表示するには

テキストボックスにキーワードを入れて、検索結果をビューで表示する、という画面の作り方を説明する。

XPageでの全文検索結果表示の基本的な考え方

NotesView形式のデータソースには、全文検索を行う機能がある。 検索ボックスで入れたキーワードを元に、データソースで検索をすませておく。

こうすればデータソースの結果がすでに全文検索で選別された状態になるので、データソースの結果を表示する通常のビューを作る方法でいいことになる。

ただ、問題は検索キーワードを元にデータソースで検索をかける方法。検索キーワードの枠の横に[検索]みたいなボタンを置いておけば、ボタン押下時に検索処理を走らせることはできる。 でも、その検索結果をビューに反映する方法を考えないといけない。

ビューコントロールに対して、「refresh」みたいな指示が出せればいいんだけど、そういう仕組みにはなってない。

そのため、検索キーワードが決定したら、グローバルな領域に検索キーワードを保存しておき、自分のページを再表示する。

再表示時に、グローバル領域から検索キーワードを取り出し、データソースで検索をかけて、それをもとにビューを表示すればうまくいくというものだ。

このグローバル領域として、sessionScopeやrequestScopeが用意されている。

検索キーワードを得る方法

検索ボタンが押されたときに起動される処理は、検索ボタンのonclickイベントに記載すればいい。 最初は、検索ボックスに入力されている値をとってくるには、検索ボックスのIDを指定して、NotesItemをgetして、値をとればいいかと思った。

ところが、XPagesの世界は基本はWebの世界だから、どうもそういうことはできないようだ。

変わりに、「バインド」という仕組みを使う。検索ボックスの値を「viewScope」というグローバル領域に格納しておく。検索ボタン側のイベント処理でも、viewScropeから読み出すことで 検索キーを得るという仕組みだ。

実際のソース

検索ボックスの値をviewScopeに格納する方法

検索ボックスの値をviewScopeに格納する方法は簡単。

  <xp:inputText id="inputText1" value="#{viewScope.searchValue}"></xp:inputText>
  

検索ボタンのイベント処理

検索ボタンのイベント処理には、以下のように書く。 検索ボックスのviewScope.searchValueをsessionScope.locationfilterに移して、自分を表示しなおす(ここではmypageが自XPageの名前)。

  <xp:button id="button1" value="検索">
    <xp:eventHandler event="onclick" submit="true" refreshMode="complete">
      <xp:this.action>
        <![CDATA[#{javascript:
          sessionScope.locationfilter = viewScope.searchValue;
          context.redirectToPage("mypage");
        }]]>
      </xp:this.action>
    </xp:eventHandler>
  </xp:button>
  

ビューコントロールの処理

ビューコントロールでは、ビュータイプのデータソースに検索条件を設定しておくだけで検索結果を表示できる(簡略化のため、リンクで文書を開く処理は取り除いてる)。 検索条件は、search="xxxx"で指定できる。ここを search="#{javascript:sessionScope.locationfilter}" のように書けば、sessionScope.locationfilter に設定されているキーワードで検索できる。 ただし、アプリケーションの全文検索を先に行っておかないと、エラーになるので注意(全文検索されてないよ、というようなエラーじゃないので知らないと解決に苦労するかも)。

ビューコントロールのデータソースはビューコントロールの外に出すのがポイントみたい。 ビューコントロールの中にデータソースを入れるパターンではうまく動作しなかった。

  <xp:this.data>
    <xp:dominoView var="管理部署別" viewName="管理部署別" search="#{javascript:sessionScope.locationfilter}" >
    </xp:dominoView>
  </xp:this.data>
  <xp:viewPanel rows="30" id="viewPanel1" value="#{管理部署別}">
    <xp:this.facets>
      <xp:pager partialRefresh="true" layout="Previous Group Next"
        xp:key="headerPager" id="pager1">
      </xp:pager>
    </xp:this.facets>
    <xp:viewColumn columnName="管理部署" id="viewColumn1">
      <xp:viewColumnHeader value="管理部署" id="viewColumnHeader1"></xp:viewColumnHeader>
    </xp:viewColumn>
    <xp:viewColumn columnName="管理者氏名" id="viewColumn2">
      <xp:viewColumnHeader value="管理者氏名" id="viewColumnHeader2"></xp:viewColumnHeader>
    </xp:viewColumn>
    <xp:viewColumn columnName="管理番号" id="viewColumn3">
      <xp:viewColumnHeader value="管理番号" id="viewColumnHeader3"></xp:viewColumnHeader>
    </xp:viewColumn>
    <xp:viewColumn columnName="ホスト名" id="viewColumn4">
      <xp:viewColumnHeader value="ホスト名" id="viewColumnHeader4"></xp:viewColumnHeader>
    </xp:viewColumn>
    <xp:viewColumn columnName="場所" id="viewColumn5">
      <xp:viewColumnHeader value="場所" id="viewColumnHeader5"></xp:viewColumnHeader>
    </xp:viewColumn>
  </xp:viewPanel>
  

ちょっと改良(直接バインド)

先ほどの例だと検索ボタンでは、わざわざ検索ボックスに設定された viewScope.searchValueをsessionScope.locationfilter に移していた。 検索ボックスのバインド先をsessionScope.locationfilterに指定してしまえば、いちいちコピーする必要もないかも。

この方法だと、検索ボックスでキーワードを入れて、Enter押しただけでも、検索がかかる。検索ボックスのイベント処理に自ページを開きなおす処理が組み込まれているのかも。

  <xp:inputText id="inputText1" value="#{sessionScope.locationfilter}"></xp:inputText>
  <xp:button id="button1" value="検索">
    <xp:eventHandler event="onclick" submit="true" refreshMode="complete">
      <xp:this.action>
        <![CDATA[#{javascript:
          context.redirectToPage("mypage");
        }]]>
      </xp:this.action>
    </xp:eventHandler>
  </xp:button>
  

ちょっと改良(requestScopeの利用)

先ほどの例では、sessionScopeを使っていたが、よく考えるとちょっと範囲が広すぎる。sessionScopeはログイン後からログアウトまで存続するので、検索に使った後はクリアするなどしないといけない。

この場合は、検索キーを設定して、次ページ(といっても自分だけど)に遷移する間だけ検索キーを保持しててくれればいいので、requestScopeを使うべきだろう。これならクリアの必要がない。

  <xp:this.data>
    <xp:dominoView var="管理部署別" viewName="管理部署別" search="#{javascript:requestScope.locationfilter}" >
    </xp:dominoView>
  </xp:this.data>
  <xp:inputText id="inputText1" value="#{requestScope.locationfilter}"></xp:inputText>
  <xp:button id="button1" value="検索">
    <xp:eventHandler event="onclick" submit="true" refreshMode="complete">
      <xp:this.action>
        <![CDATA[#{javascript:
          context.redirectToPage("mypage");
        }]]>
      </xp:this.action>
    </xp:eventHandler>
  </xp:button>
  

課題

ちょっと気になることがある。

search="#{javascript:requestScope.locationfilter}" と書かれているが、requestScope.locationfilter に何かJavaScriptのコードっぽいものを入れられると、変な処理が走ってしまいそうで怖い。

キーワード入力時に、そういう危ない文字列をはじく処理を入れないといけないと思うのだがどうやるんだろう。