Such-Portlet

Mit Hilfe des Such-Portlets lassen sich Suchformulare und Trefferlisten in eine Website integrieren.

Sowohl die Funktion als auch das Layout der Suche können angepasst werden, ohne dass Java-Code geändert zu werden braucht. Das Portlet kann sowohl Anfragen an den Content Management Server als auch an den Search Server senden. Es verwendet die in der Konfigurationsdatei pm.xml im dem Eintrag searchEngine konfigurierte Suchmaschine.

Jede Suche wird in der Portlet-Web-Applikation in einem Verzeichnis unterhalb des instanzenspezifischen Web-Applikationsverzeichnisses WEB-INF/templates/search definiert.

Verwendung

Das Such-Portlet wird folgendermaßen in Layoutdateien eingebunden:

<npspm includeportlet="/PM-PL/search" instance="dealer" language="de" />

Der Instanzenname dealer entspricht hierbei dem Namen des Verzeichnisses, das die Definition der Suche enthält. Mit language kann optional die vom Portlet zu verwendende Sprache angegeben werden. Fehlt diese Angabe, wird die im Browser eingestellte Sprache verwendet.

Konfiguration

Definition einer Suche

Eine Suche wird mit Hilfe zweier Velocity-Templates definiert, die sich im oben angegebenen Verzeichnis unterhalb der jeweiligen Web-Applikation befinden müssen. Die Datei view.vm legt das Layout des Suchformulars und der Trefferliste fest. Mit Hilfe der Datei config.vm wird die vom Portlet auszuführende Suchanfrage definiert.

Da das Portlet sich mehrsprachig darstellen kann, wird je Sprache bzw. Locale der Verity-Suchmaschine eine Lokalisierungsdatei mit dem Namen localizer_xy.properties benötigt, wobei xy im obigen Dateinamen durch das jeweilige Sprachkürzel ersetzt werden muss. Diese Dateien enthalten benannte Zeichenketten in der folgenden Form:

title: Händler
buttonSearch: Suchen
errorNoResults: Ihre Suchanfrage hat keine Ergebnisse geliefert.

Beachten Sie bitte, dass die Kodierung der Localizer-Dateien UTF-8 sein muss.

Aufbau der Konfigurationsdatei config.vm

Nachdem das vom Portlet generierte Suchformular abgeschickt wurde, lädt und evaluiert das Portlet das Velocity-Template config.vm, um aus den Eingaben im Formular die Suchanfrage zu erzeugen. Die Parameter aus dem Suchformular stehen im Template unter ihrem Namen zur Verfügung.

Die Auswertung des Templates ergibt ein XML-Dokument, aus dem das Portlet ein Suchanfrage-XML-Dokument erzeugt, das der Search Server verarbeiten kann. Das Portlet sendet dieses Dokument an den Search Server und erhält als Antwort ein XML-Dokument, das die anzuzeigenden Suchergebnisse enthält.

Das vom Velocity-Template erzeugte XML-Dokument muss die folgende Struktur haben:

<query>
  <condition>...</condition>
  <result-fields>...</result-fields>
  <sort>...</sort>
  <collections>...</collections>
  <page-size>...</page-size>
  <max-hits>...</max-hits>
  <min-score>...</min-score>
</query>

Mit Ausnahme der condition sind alle Elemente optional. Hier als Beispiel die Datei config.vm für eine Suche nach Dokumenten, die einen Suchbegriff enthalten, und die für jeden Benutzer frei zugänglich sind:

<query>
  <condition>
    <and>
      <vql-statement>
        &lt;#MANY&gt;&lt;#STEM&gt;${searchText}
      </vql-statement>
      <vql-statement>
        &lt;#MANY&gt;&lt;#STEM&gt;free &lt;#IN&gt;
        noPermissionLiveServerRead
      </vql-statement>
    </and>
  </condition>
  <result-fields>
    <field>title</field>
    <field>name</field>
    <field>visiblePath</field>
    <field>ip_abstract</field>
  </result-fields>
  <sort>
    <criterion>
      <field>pl_PLZ</field>
      <ascending/>
    </criterion>
    <criterion>
      <field>name</field>
      <ascending/>
    </criterion>
  </sort>
  <page-size>5</page-size>
  <collections>
    <collection>cm-contents-${language}</collection>
  </collections>
  <log>
    <context>search</context>
#if ($user.isLoggedIn())
    <login>${user.login}</login>
#end
    <query>${searchText}</query>
 </log>
</query>

Die Elemente der Suchanfrage haben die im Folgenden erläuterte Bedeutung:

  • condition: Dies legt die Suchanfrage fest. Die condition kann die folgenden Operatoren enthalten:
    • and: dieses Element verknüpft die darin enthaltenen Elemente logisch mit und.
    • contains: sucht nach Dateien, bei denen das Feld field die Zeichenkette value enthält. Beispiel:
      <contains>
        <field>keywords</field>
        <value>Dienstleistung</value>
      </contains>
    • contains-match: sucht nach Dateien, bei denen das Wildcard-Muster in value auf den Wert des Feldes field passt. Erlaubte Wildcards sind Stern (*) und Fragezeichen (?).
    • equals: sucht nach Dateien, bei denen das Feld field exakt dem Wert value entspricht. Beispiel:
      <equals>
        <field>name</field>
        <value>mastertemplate</value>
      </equals>
    • or: verknüpft die im Element enthaltenen Elemente logisch mit oder.
    • starts-with: sucht nach Dateien, bei denen das Feld field mit der Zeichenkette value beginnt.
    • vql-statement: enthält die Suchanfrage in der expliziten Verity-Anfrage-Sprache. Details zur Syntax finden Sie im Handbuch zum Search Server. Beachten Sie bitte, dass Sonderzeichen als HTML-Entities angegeben werden müssen, also beispielsweise < als &lt;.
  • result-fields: Dieses Element spezifiziert in seinen field-Unterelementen die Felder der gefundenen Dokumente, die ins Suchergebnis aufgenommen werden sollen, damit sie auf den Ergebnisseiten angezeigt werden können.
  • sort: Hier lassen sich ein oder mehrere Kriterien spezifizieren, nach denen die Treffer sortiert werden sollen. Jedes Kriterium besteht aus dem Feldnamen field, dessen Inhalt zur Sortierung herangezogen wird, sowie optional einem der Elemente ascending oder descending, um die Sortierreihenfolge zu bestimmen.
  • page-size: Mit diesem Element kann die Anzahl der Treffer angegeben werden, die das Portlet je Ergebnisseite anzeigen soll.
  • collections: Geben Sie hier die zu durchsuchenden Collections an. Wird diese Angabe weggelassen, werden alle Collections durchsucht.
  • log: Geben Sie hier die zu protokollierenden Einträge an. Mögliche Einträge sind context, login und query. Der Eintrag context muss angegeben sein, andernfalls findet keine Protokollierung statt.

Bitte beachten Sie, dass die zu durchsuchenden und auch die von der Suchmaschine zurückzugebenden Felder (siehe oben result-fields, sort ) in der Verity-Konfiguration definiert worden sein müssen, bevor die betreffende Collection erzeugt wird (siehe die Dokumentation zur Search Cartridge).

Aufbau der Konfigurationsdatei view.vm

Mit diesem Velocity-Template wird der HTML-Text des Suchformulars und der Trefferliste generiert.

Mit den folgenden Schlüsselwörtern kann man im Template auf Objekte im Velocity-Kontext zugreifen:

  • $action: Die im Suchformular zu verwendende Aktion.
  • $params: Liste der bisherigen Suchparameter. Interne Parameter (deren Name beginnt mit einem Unterstrich) tauchen in dieser Liste nicht auf.
  • $text: bietet Zugriff auf lokalisierte Texte (aus den Lokalisierungsdateien im Definitionsverzeichnis).
  • $locale: das aktuelle Locale.
  • $search: Tool, mit dem URLs erzeugt werden können. Die folgenden Methoden sind verfügbar:
    • String getPageUrl(Page page): liefert eine URL, mit der auf die angegebene Seite der Trefferliste gesprungen werden kann.
    • String getSearchUrl(String parameter, String value): liefert eine URL, mit der eine Suche mit dem angegebenen Parameter durchgeführt werden kann.
    • String highlight(String word, String text, String prefix, String suffix): Klammert jedes Vorkommen des angegebenen Wortes word im Text text in die Zeichenketten prefix und suffix ein.
  • $result: bietet mit den folgenden Methoden Zugriff auf die Trefferliste:
    • int getHitCount(): liefert die Anzahl der Treffer.
    • List getPages(): liefert die Liste der Page-Objekte im Suchergebnis.
    • Page getCurrentPage(): liefert das Page-Objekt, das die aktuelle Seite der Trefferliste repräsentiert.

Die Eigenschaften der oben aufgeführten Objekte sind in der mit dem Portlet gelieferten javadoc-Dokumentation beschrieben. Beispiel (oben das Formular, unten das Verlocity-Template):

<form method="get" action="$action">
  <input type="text" name="searchText" value="$!params.searchText"/>
  <input type="submit" name="_buttonSearch"
    value="$text.buttonSearch"/>
</form>

#if ($result)
  #if ($result.hitCount == 0)
    $text.errorNoResults
  #else
    <ul>
    #foreach ($item in $result.currentPage.hits)
      #set($path = $document.getUrl($item.visiblePath))
      <li><a href="$path">$item.title</a></li>
    #end
    </ul>
  #end
#end

Den Zugriff auf die Suchmaschine konfigurieren

Die Suchmaschine wird in der Datei pm.xml als bean searchEngine konfiguriert. Es stehen zwei Implementierungen zur Auswahl, der direkte Zugriff über den Search Server und der Zugriff über den Content Management Server.

SesSearchEngine: Für die Suche auf dem Live-System werden die Suchanfragen direkt an den dort laufenden Search Engine Server geschickt:

<bean id="searchEngine"
    class="com.infopark.libs.search.ses.SesSearchEngine">
  <property name="host"><value>localhost</value></property>
  <property name="port"><value>3011</value></property>
</bean>

AdvancedCmSearchEngine: Bei der Suche über den Content Management Server wird das Redaktionssystem durchsucht. Der Content Management Server leitet die Anfragen an den für das Redaktionssystem zuständigen Search Engine Server weiter, fügt zu jedem Treffer jedoch den Pfad der gefundenen CMS-Datei hinzu:

<bean id="searchEngine"
    class="com.infopark.libs.search.cm.AdvancedCmSearchEngine">
  <property name="host"><value>localhost</value></property>
  <property name="port"><value>3002</value></property>
  <property name="user"><value>root</value></property>
  <property name="tokenManager">
    <ref bean="tokenManager"/>
  </property>
</bean>

Beispiel

Das folgende Beispiel zeigt, wie eine neue Suche im Democontent-Portal erstellt wird. Ein einzugebender Suchbegriff soll im Hauptinhalt und im Titel sowie in der Zusammenfassung gesucht werden, also in den Feldern mit den Namen blob, title und ip_abstract. Auf den Ergebnisseiten sollen die Felder title und ip_abstract ausgegeben werden. Die Zusammenfassung soll nur angezeigt werden, wenn sie Text enthält, also nicht leer ist.

Vorbereitung der Suchmaschinen-Indizes

Die oben genannten Felder können erst verwendet werden, nachdem die Konfiguration der Suchmaschine auf der Live-Seite so erweitert wurde, dass die zusätzlichen Felder zur Verfügung stehen und beim Indizieren der Dokumente gefüllt werden.

Öffnen Sie hierfür die zu der Instanz und der betreffenden (zu durchsuchenden) Collection gehörende Datei

  instance/instanceName/config/vdk/styles/collectionName/style.ufl

und tragen Sie zuätzlich zu den bestehenden Feldern die folgenden Felder ein:

# -----------------------------------------------------------------
# Specify additional application-specific fields here in their own
# data-table[s].

data-table: nps
{
  **** Bisherige Felder stehen hier
  ****
  **** Neue Felder

  varwidth:   ip_abstract dxa
  autoval:    collection DBNAME
}

Stoppen Sie vor den nachfolgenden Schritten den Search Engine Server:

./rc.npsd stop SES

Damit die Änderungen wirksam werden, muss die Collection neu angelegt und die Dokumente müssen neu indiziert werden. Starten Sie hierfür den Search Engine Server im Singlemodus:

./SES -single

Führen Sie nun die folgenden Schritte für die betreffende Collection aus:

% deleteCollection collectionName
% createCollection collectionName
% exit

Starten Sie den Search Engine Server:

./rc.npsd start SES

Melden Sie sich nun am Content Management Server an:

./client localhost 3001 root demo

Indizieren Sie mit dem folgenden Kommando alle NPS-Dateien neu:

CM>indexAllObjects
CM>exit

Die neue Suche erstellen

Wechseln Sie in das Verzeichnis

/instance/instanceName/webapps/PM-PL/WEB-INF/templates/search

und legen Sie unter dem Namen bodySubjects eine Kopie des vorhandenen Verzeichnisses nameTitle an. Wechseln Sie nun in den Ordner und bearbeiten Sie die beiden Dateien config.vm und view.vm.

In der Datei config.vm wird die Suchanfrage konfiguriert. Ändern Sie zu diesem Zweck den Inhalt des condition-Elements so, dass er nur das folgende vql-statement-Element enthält:

<vql-statement>
  (&quot;${suche}&quot; &lt;#IN&gt; blob) &lt;#OR&gt;
  (title &lt;#SUBSTRING&gt; &quot;${suche}&quot;) &lt;#OR&gt;
  (ip_abstract &lt;#SUBSTRING&gt; &quot;${suche}&quot;)
</vql-statement>

Beachten Sie, dass die Suchanfrage aus mehreren Teilen (eines pro Feld) besteht, die miteinander mit OR verknüpft sind. Da der blob (der Hauptinhalt) eine Dokumentzone und kein Feld ist (wie title und ip_abstract), wird für ihn der Operator IN verwendet, der Dokumentzonen durchsucht. Der Operator SUBSTRING dagegen durchsucht Feldinhalte.

Da in den Suchergebnissen die Inhalte des Feldes ip_abstract vorkommen sollen, muss das Element result-fields um dieses Feld ergänzt werden:

<result-fields>
  <field>objId</field>
  <field>name</field>
  <field>title</field>
  <field>score</field>
  <field>visiblePath</field>
  <field>ip_abstract</field>
</result-fields>

Die Such- und Ergebnisseite erstellen

Die Such- und Ergebnisseite werden in der Datei view.vm zusammengestellt. Der obere Teil definiert das Suchformular:

<form action="$action" method="post">
  <div>
    Ihre Suche <input type="text" name="suche"
        value="$!params.suche" />
  </div>
  <input type="submit" name="dialog.buttonOk"
      value="$text.buttonOk" />
</form>

Der untere Teil der Datei view.vm sorgt dafür, dass die Ergebnisse angezeigt werden, nachdem der Benutzer das Suchformular abgeschickt hat. Alle Ergebnisse sollen als Liste dargestellt werden, die je Treffer dessen Titel (verlinkt) und die Zusammenfassung enthält, also in folgender Form:

<ul>
  <li>Verlinkter Titel<br />
  ip_abstract</li>
  <li>...</li>
  <li>...</li>
</ul>

Um diese Darstellung zu erreichen, wird der folgende Code verwendet:

#if ($result)
  #if ($result.hitCount == 0)
    <div>$text.noHits</div>
  #else
    <!-- Anzahl der Ergebnisse -->
    <div>$text.hitCount $result.hitCount</div>
    <!-- Beginn Auflistung der Ergebnisse -->
    <ul>
      #foreach ($item in $result.currentPage.hits)
        <li>
          #set($path = $document.getUrl($item.visiblePath))
          #if ($path)<a href="$path">#end
          $item.title
          #if ($path)</a>#end
          #if ($item.ip_abstract)<br />$itm.ip_abstract#end
        </li>
     #end
    </ul>
    <!-- Ende Auflistung der Ergebnisse -->
    <!-- Links zu anderen Ergebnisseiten erzeugen -->
    <div>
    #foreach ($page in $result.pages)
      #if ($page.isCurrent())
        $page.number
      #else
        <a href="$search.getPageUrl($page)">$page.number</a>
      #end
    #end
    </div>
    <div>$text.page $result.currentPage.number $text.pageOf
        $result.pages.size()
    </div>
    <!-- Ende Übersicht Ergebnisseiten -->
  #end
#end

Um die Pfade in der Ergebnisliste vom Anfang her um ein Verzeichnis zu kürzen, so dass ein übergeordneter Ordner nicht angezeigt wird, verwenden Sie in der foreach-Schleife den folgenden Code:

## Verzeichnistrenner (slash) suchen
#set( prefix $path.indexOf("/", 1) )
## Statt mit $path folgendermaßen auf den Pfad zugreifen
$path.substring($prefix + 1)

Das Portlet in eine Webseite einbinden

Um das Portlet in eine Webseite einzubinden, fügen Sie bitte die folgende Zeile in eine Layoutdatei ein, mit der anzuzeigende Seiten erzeugt werden:

<npspm includePortlet="/PM-PL/search" instance="bodySubjects" />

In der separaten Vorschau wird nun das Portlet angezeigt und kann verwendet werden.

Eingabefelder des Suchformulars leeren

Das Suchportlet speichert die in seinem Suchformular enthaltenen Eingaben. Ruft ein Website-Besucher nach einer Suche erneut die Suchseite auf (die Seite, die das Suchportlet enthält), so enthält das Formular die letzten Eingaben, auch wenn die Suchseite zwischenzeitlich verlassen wurde.

Da dieses Verhalten normalerweise nicht erwünscht ist, kann man das Portlet die Eingabefelder leeren lassen. Bis Version 6.0.x fügen Sie hierzu in die URL auf die Suchseite den Parameter reset=true hinzu. Beispiel:

<a href="/suchen.html?reset=true">Suche</a>

Ab Version 6.5.1 fügen Sie stattdessen einen Link auf das Remote-Control-Portlet ein. Beispiel:

<npspm includePortlet="/PM-PL/remoteControl" instance="search">
targetUrl=/suchen.html
emptySearch=true
emptySearchLinkText=Zur Suche
</npspm>

Der verlinkte Text ist im Portlet selbst konfiguriert und lautet "Neue Suche". Wenn Sie den verlinkten Text wie im obigen Beispiel mit dem Parameter emptySearchLinkText beim Portlet-Aufruf angeben können möchten, ersetzen Sie bitte in der zum Control-Portlet gehörenden Template-Datei webapps/PM-PL/WEB-INF/templates/remote/search/view.vm die Zeile

  <a href="javascript:document.emptySearch.submit();">$text.emptySearch</a>

durch folgenden Code:

#if ($params.emptySearchLinkText)
    #set ($linkText = $params.emptySearchLinkText)
#else
    #set ($linkText = $text.emptySearch)
#end
    <a href="javascript:document.emptySearch.submit();">$linkText</a>