Url Link

JCMS 5.5 : Développement de recherches étendues avec l'API des QueryFilter

In brief...
Cet article décrit les possibilités offertes par les QueryFilter de JCMS 5.5 pour étendre les recherches à des sources d'information externes (base de données, bases documentaires, moteurs de recherche, …) et, d'une manière plus générale, pour agir sur les recherches et les résultats obtenus. Ces principes sont illustrés avec l'ouverture des recherches textuelles à Google.
Subject Integration & Compatibility
Jalios API
Products JCMS 5.5
Published 5/2/06
Writer Olivier Dedieu
Le développement des QueryFilter a été mis à jour dans JCMS 5.7. Si vous utilisez cette version, reportez-vous à l'article JCMS 5.7 : Développement de recherches étendues avec l’API des QueryFilter.

1. Introduction

JCMS 5.5 fournit en standard deux mécanismes de recherche :

  1. Recherche dans les publications gérées par JCMS. Ces recherches peuvent combiner plusieurs critères de différentes natures (texte, type de publication, catégorie, dates, auteur, langues, ...).
  2. Recherche dans les pièces jointes. Il s'agit de recherche en texte intégral sur le contenu des pièces jointes et elles se font en exploitant des indexes Lucene alimentés par le module JCMS Universal.

Dans JCMS 5.5, toutes les recherches de publications sont prises en charges par la classe com.jalios.jcms.QueryManager. Celle-ci gère aussi bien les recherches déclenchées par des utilisateurs que les recherches effectuées par une Portlet Requête/Iteration pour afficher ses résultats. Il est important de noter que l'interface de présentation des résultats de JCMS ne sait traiter que des objets Publication. L'ensemble des résultats produits par le QueryManager ne doit donc être composé que d'instance de Publication. Pour les pièces jointes, ce n'est donc pas le fichier qui est retourné mais le FileDocument le référençant (et si ce FileDocument est lui-même référencé par un contenu, c'est ce contenu qui est mis dans la liste des résultats).

L'API QueryFilter permet de compléter ces mécanismes standards en agissant sur les paramètres d'une recherche et en modifiant l'ensemble de résultats obtenus par la méthode standard de recherche. A partir de JCMS 5.5, l'API permet aussi d'intégrer les QueryFilter aux interfaces de recherche sans avoir à modifier les JSP.

2. Les QueryFilter

2.1 Principes de fonctionnement

Un QueryFilter permet d'agir en amont (paramétrage de la recherche) et en aval (liste des résultats) de toute recherche. Pour chaque recherche, les QueryFilter sont invoqués par le QueryManager pour 1) agir sur les paramètres de la requête, 2) enrichir ou restreindre l'ensemble des résultats obtenus par la recherche JCMS. Dans le cadre d'une recherche étendue à des sources externes, on enrichira l'ensemble des résultats avec les résultats externes. Mais il peut aussi être souhaitable de réduire l'ensemble des résultats. Par exemple, si les résultats contiennent plusieurs éléments appartenant à un même objet composite (p. ex. des FaqEntry qui composent une Faq, des objets Chapitre qui composent un objet Livre, …), il est possible de retirer ces résultats et de les remplacer par l'objet composite qui leur correspond.

L'extension de la recherche à une source d'information externe, consiste donc à développer un QueryFilter pour cette source. A priori il n'agira pas sur les paramètres de la requête mais enrichira les résultats de la recherche des résultats obtenus en interrogeant la source. Ce QueryFilter étant invoqué à chaque appel de la méthode query() (p. ex. pour l'affichage du contenu d'une portlet Requête/Itération),un paramètre supplémentaire dans la requête de recherche est ajouté pour déterminer si la source externe doit être utilisée. Avec JCMS 5.5, l'API des QueryFilter prend en charge la gestion de ce paramètre.

L'ensemble des résultats retournés par le QueryManager ne pouvant contenir que des instances de la classe Publication, le QueryFilter doit encapsuler les résultats obtenus sur la source externe dans des Publication. Pour cela, on utilise des objets transitoires (c'est-à-dire, non stockés dans le store). Ces objets doivent néanmoins avoir certains attributs tels qu'un identifiant, une cdate, une mdate, ... La méthode prepareExternalResult() de la classe QueryManager sert à remplir les champs d'une instance transitoire. Si les résultats peuvent être exprimés sous forme d'un titre, une description et une URL, il est possible d'utiliser le type WebPage. Sinon, il faut développer un type de contenu dédié à la présentation de ces résultats.

Enfin, lorsque le résultat affiché est un extrait d'une donnée plus détaillée, il convient de fournir à l'utilisateur un moyen de consulter la donnée complète. Si cette donnée est accessible par une URL, il suffit de mettre un lien dans le résultat de recherche, sinon il faut prévoir une JSP capable d'extraire et de présenter la donnée.

2.2 La classe QueryFilter

La classe abstraite com.jalios.jcms.QueryFilter comporte deux groupes de méthodes : celles permettant de filtrer la recherche et (nouveauté de JCMS 5.5) celles permettant d'intégrer le QueryFilter dans les interfaces de recherche.

2.2.1 Les méthodes agissant sur la recherche

  1. public QueryHandler filterQueryHandler(QueryHandler qh, Map context)
    Cette méthode reçoit en paramètre le QueryHandler (qh) et doit fournir en retour un QueryHandler. Un QueryHandler est un objet contenant l'ensemble des critères de recherche. L'implémentation par défaut renvoie le qh reçu. Cette méthode ne doit être surchargée que si les paramètres de recherches doivent être modifiés (par exemple pour modifier le texte recherché, pour agir sur les catégories, les types, ...)
    Le paramètre contexte permet de passer des informations entre l'appel à cette méthode et l'appel à filterResultSet().
  2. public QueryResultSet filterResultSet(QueryHandler qh, QueryResultSet set, Map context)
    Cette méthode reçoit en paramètre le QueryHandler qui a été utilisé pour traiter la requête (qh) et l'ensemble des résultat obtenus (set) par JCMS. Elle doit fournir en retour un ensemble de résultat. L'implémentation par défaut renvoie le set reçu. C'est typiquement dans cette méthode que l'on interroge la source d'information externe et que l'on ajoute des résultats à l'ensemble des résultats fournit.
    Le paramètre context permet de récupérer des informations positionnées par la méthode filterQueryHandler().

La classe QueryResultSet est une nouvelle classe de l'API JCMS 5.5. Elle dérive de la classe java.util.HashSet. Elle contient non seulement les résultats d'une recherche mais aussi le score permettant de calculer les indices de pertinence.

La méthode utilitaire prepareExternalResult(Publication pub) de la classe QueryManager, remplit les champs (id, cdate, mdate, pdate, author) de la publication reçue afin de la rendre exploitable lors de l'affichage des résultats.

2.2.2 Les méthodes d'insertion dans l'interface de recherche

Ces méthodes doivent être surchargée pour le QueryFilter soit proposé de façon optionnelle dans les interfaces de recherche de JCMS.

  1. public String getSearchParam()
    Cette méthode doit renvoyer le nom du paramètre HTTP indiquant que le QueryFilter devra être pris en compte pour cette recherche.
  2. public String getSearchLabel(String lang)
    Cette méthode doit renvoyer le libellé du QueryFilter (le libellé peut contenir du code HTML).
  3. public String getSearchDesciption(String lang)
    Cette méthode doit renvoyer la description du QueryFilter.
  4. public boolean getSearchDefault()
    Cette méthode doit renvoyer true si le QueryFilter doit être sélectionné par defaut, false sinon.

2.2.3 Déclaration des QueryFilter

Les QueryFilter doivent être déclarés dans JCMS en invoquant la méthode addQueryFilter() de la classe QueryManager. Ces déclarations se font habituellement dans la méthode initQueryFilter() de la classe custom.JcmsInit.

public void initQueryFilter() {
QueryManager queryMgr = Channel.getChannel().getQueryManager();
QueryFilter qf = new custom.MyQueryFilter();
queryMgr.addQueryFilter(qf);
}

A noter qu'avec l'API JCMS 5.5, le constructeur de QueryFilter peut recevoir un entier indiquant l'ordre d'appel du QueryFilter. Ceci permet de définir l'ordre dans lequel seront invoqués les QueryFilter.

3. Exemple : extension de la recherche à Google

3.1 Principes de fonctionnement

Cet exemple illustre l'ouverture de la recherche textuelle à Google. L'interface de recherche proposera donc une nouvelle option permettant d'étendre ou non la recherche à Google.

google search

Fig. 1. Formulaire de recherche avec l'option de recherche dans Google

Lorsque cette option est cochée lors d'une recherche textuelle, les résultats comprendront non seulement des contenus de JCMS mais aussi les 10 premiers sites retournés par Google.

google results

Fig. 1. Exemple de résultats de recherche

 

Pour présenter les résultats issus de Google, on utilise des objets WebPage avec un gabarit de recherche dédié. Ce gabarit est déclaré dans le type WebPage.

google webpage

Fig. 3. Déclaration du gabarit de présentation Google

Ce gabarit est assez proche du gabarit de présentation synthétique des WebPage mais ajoute le logo Google pour distinguer ces résultats des autres et le lien du titre amène sur le site référencé au lieu d'amener sur la vue détaillée de la WebPage.

Cet exemple est téléchargeable sous forme d'un fichier zip déployable sur une webapp JCMS 5.5.0-fr vierge.

3.2 Google Web APIs

Les recherches dans Google sont réalisées via la Google Web APIs. Il s'agit d'une bibliothèque Java permettant d'effectuer des recherches sur les sites indexés par Google. Bien qu'elle soit distribuée gratuitement, son utilisation requiert une clé qui peut être obtenue lors du téléchargement de l'API. Cette clé donne droit à 1000 requêtes/jour. Au-delà, un accord commercial doit être établi avec Google.

Pour cet exemple, la clé est déclarée dans le fichier WEB-INF/data/webapp.prop :

# GoogleQueryFilter - the key for the GoogleAPI
google.key: abcdefg12345
# GoogleQueryFilter - description
en.google.description: Extends search to Google
fr.google.description: Etendre la recherche à Google

3.3 La classe GoogleQueryFilter

La classe GoogleQueryFilter est chargée d'étendre les recherches textuelle à Google.

package custom;
import java.util.*;
import org.apache.log4j.Logger;
import com.google.soap.search.*;
import com.jalios.jcms.*;
import com.jalios.jcms.handler.*;
import com.jalios.util.*;
import generated.WebPage;

public class GoogleQueryFilter extends QueryFilter {
private static final Logger logger = Logger.getLogger(GoogleQueryFilter.class);

QueryManager queryMgr;
String key;
int maxResults = 10;

public GoogleQueryFilter() {
queryMgr = Channel.getChannel().getQueryManager();
// Get the Google key
key = Channel.getChannel().getProperty("google.key", null);
if (Util.isEmpty(key)) {
logger.warn("No Google key");
return;
}
logger.info("GoogleQueryFilter is enabled");
}
/**
* This method should be overriden to filter the result set
*
* @param qh the QueryHandler
* @param set the result set
* @param context the context. Can be used to exchange data between custom QueryFilters.
* @return the (filtered) result set
* @since jcms-5.0.0
*/
public QueryResultSet filterResultSet(QueryHandler qh, QueryResultSet set, Map context) {

// Check the Google key
if (Util.isEmpty(key)) {
logger.warn("No Google key");
return set;
}

// Skip non-textual search
String text = qh.getText();
if (Util.isEmpty(text)) {
return set;
}
// Check if the request must be extended to Google
if (Util.isEmpty(qh.getRequest().getParameter("google"))) {
return set;
}
try {
// Prepare Google search
GoogleSearch search = new GoogleSearch();
search.setKey(key);
search.setQueryString(text);
search.setMaxResults(maxResults);
// Start Google search
logger.info("Start google search for: " + text);
GoogleSearchResult googleResults = search.doSearch();
// Add Google results
GoogleSearchResultElement[] elts = googleResults.getResultElements();
for(int i = 0; i < elts.length; i++) {
GoogleSearchResultElement elt = elts[i];

// Encapsulate the result in a WebPage
WebPage result = new WebPage();
queryMgr.prepareExternalResult(result);
result.setTitle(elt.getTitle());
result.setDescription(elt.getSnippet());
result.setUrl(elt.getURL());
result.setQueryTemplate("google");
// Add this WebPage to the result set
set.add(result);
}
} catch (GoogleSearchFault ex) {
logger.warn("The call to the Google Web APIs failed:" + ex.toString());
}

return set;
}

public String getSearchParam() {
return "google";
}
public String getSearchLabel(String lang) {
return "<img src='http://www.google.com/logos/Logo_25wht.gif' align='absmiddle' alt='Google'>";
}
public String getSearchDescription(String lang) {
return JcmsUtil.glp(lang, "google.description");
}

public boolean getSearchDefault() {
return false;
}
}

Le constructeur récupère la clé via la propriété google.key. La méthode filterResultSet() commence par vérifier qu'il s'agit bien d'une recherche textuelle qui doit être étendue à Google. Si c'est le cas, une requête de recherche est envoyée à Google. Pour chaque résultat obtenu, on construit une WebPage qui décrit le site (titre, URL et description) et qui utilise le gabarit d'affichage synthétique google (associé au JSP doGoogleResult.jsp). Cette WebPage est ensuite ajoutée dans l'ensemble des résultats.

Enfin, une instance de GoogleQueryFilter est déclarée dans JcmsInit :

  public static void initQueryFilter() {
QueryManager queryMgr = Channel.getChannel().getQueryManager();
queryMgr.addQueryFilter(new GoogleQueryFilter());
}

Références :


Member Discussions

Login   Home   fr en
JALIOS SA - SIREN 440 126 035