jeudi 22 octobre 2009

Recherche par facets avec Bobo-Browse

Bonjour,

Donc la situation est la suivante. Chez TELUS nous avons un produit de catalogue électronique qui utilise Lucène comme moteur de recherche. Mon boss me demande si on peut faire du filtrage par catégorie, c'est à dire, afficher, pour un résultat de recherche, le nombre de produits pour chaque catégorie du site, ce qui permet de faire un rafinage de la recherche de type "drill-down". Donc je cherche un peu et je tombe sur Solr naturellement, mais après lecture sommaire, je trouve ca plutôt complexe et étant donné que nous avons déjà du code qui roule en production et que je ne désire pas me lancer dans une refonte majeur de l'indexation, je continue de chercher. Finalement je tombe sur Bobo Browse, hébergé sur Google Code. Même si le nom m'arrête un peu, je continue de lire un peu et ca a effectivement l'air de correspondre à ce que je veux.

Donc je fais le tour du site, il y a un peu de documentation, et je fini par tomber sur un exemple avec des données sur les autos. Donc je me lance, je checkout le projet pour télécharger les exemples sur mon poste, car ils ne semblent pas venir avec le jar de la distribution. Je lance le build ant avec un run-cardemo et l'application se lance, mais bang, ca plante au chargement. Je suis sous windows et il semble y avoir un problème avec les backslash (\) du path. J'édite la propriété "bobo.root" du build ant pour hardcoder un path avec des slash (/) et je remplace location par value pour éviter que ant remplace mon slash par un backslash et la ca fonctionne. La démo semble rapide, même avec beaucoup de données et ca semble faire exactement ce que je veux que ca fasse, donc je creuse encore.
<property name="bobo.root" value="c:/java/bobo" />
Prochaine étape, comprendre la démo et extraire le code dont j'ai besoin.

On comprend rapidement qu'il faut fournir à bobo-browse une liste de facet, chaque facet possédant un type, soit SINGLE (value simple), MULTI (valeurs multiple), PATH (valeurs hiérarchique) ou RANGE (interval). On peut ensuite lancer un recherche, ou fixer les valeurs de certains facet, par exemple pour simuler une sélection de l'utilisateur. Si on n'a qu'une facet, fixer une sélection ne sert évidemment à rien (sauf pour les facets de type PATH), mais quand on a plusieurs facets, fixer une valeur permet de faire un drill-down et de préciser les valeurs des autres facets. Aussi, pour les facets de type PATH, fixer une valeur permet de compter les résultats pour les valeurs des sous éléments de la hiérarchie.

Donc maintenant, voici un exemple. J'ai d'abord un index de basé déjà construit qui contient des documents avec 3 champs, soit "text", "author" et "tags". Le champ "tags" contient 2 valeurs alors que les 2 autres, une seul valeur. Il existe 4 valeurs de tags utilisé et 4 auteurs et j'ai 500 documents dans l'index.

Premièrement il faut construire notre reader et nos facets, author et tags
IndexReader reader = IndexReader.open(directory);
Collection handlers = new ArrayList();
handlers.add(new SimpleFacetHandler("author", "author"));
handlers.add(new MultiValueFacetHandler("tag", "tags"));
BoboIndexReader boboReader = BoboIndexReader.getInstance(reader, handlers);
Ensuite, nous devons créer notre requête bobo-browse

//Objet de requete
BrowseRequest br = new BrowseRequest();
br.setCount(50);
br.setOffset(0);

//Query Lucene
QueryParser parser =
new QueryParser("text", new StandardAnalyzer());
Query q = parser.parse("Lucene");
br.setQuery(q);

//Facets utilisé pour cette requête
FacetSpec authorSpec = new FacetSpec();
authorSpec.setOrderBy(FacetSortSpec.OrderHitsDesc);
br.setFacetSpec("author", authorSpec);
FacetSpec tagSpec = new FacetSpec();
tagSpec.setOrderBy(FacetSortSpec.OrderHitsDesc);
br.setFacetSpec("tag", tagSpec);

//Execution de la requete
Browsable browser = new BoboBrowser(boboReader);
BrowseResult result = browser.browse(br);

//Parcours des résultats
int totalHits = result.getNumHits();
System.out.println("Total: " + totalHits);

BrowseHit[] hits = result.getHits();
System.out.println("Hits : " + hits.length);

System.out.println("Facet tag:");
Map facetMap = result.getFacetMap();
FacetAccessible tagFacets = facetMap.get("tag");
List facets = tagFacets.getFacets();
for (Iterator it = facets.iterator(); it.hasNext();) {
BrowseFacet browseFacet = (BrowseFacet) it.next();
System.out.println(browseFacet.getValue()
+ " : " + browseFacet.getHitCount());
}

System.out.println("Facet author:");
FacetAccessible authorFacets = facetMap.get("author");
facets = authorFacets.getFacets();
for (Iterator it = facets.iterator(); it.hasNext();) {
BrowseFacet browseFacet = (BrowseFacet) it.next();
System.out.println(browseFacet.getValue()
+ " : " + browseFacet.getHitCount());
}


Voici la sortie du programme
Total: 500
Hits : 50
Facet category:
information : 288
opinion : 264
idea : 236
faq : 212
Facet author:
Lemieux : 156
Durand : 132
Gagnon : 108
Larouche : 104

Si on fait la somme des valeurs des auteurs, on obtient 500, chaque document ayant un auteur et la somme des tags donne 1000, chaque document ayant 2 tags.

Bonne chance avec vos expérimentation bobo-browse.

vendredi 18 septembre 2009

Débugger jQuery avec FireQuery

FireQuery est un addon a FireBug qui permet de voir dans la vue HTML, les éléments ajouté par jQuery aux tags de votre page. On peut ainsi voir les events handler et autres trucs.

Autre modification intéressante, on peut lancer des requetes dans la console Firebug et les résultats sont présenté de façon plus lisible. À essayer.

Finalement, un bouton jQuerify permet d'injecter jQuery dans un site qui ne l'aurait pas, pour faire des tests avec la console.

http://firequery.binaryage.com/

samedi 20 juin 2009

Web Services Standards

InnoQ vient de sortir un superbe poster schématisant les standards au niveau des service Web. Après cela, on me dit que c'est simple des services Web ....

http://www.innoq.com/resources/ws-standards-poster/

mercredi 13 mai 2009

Vous avez abandonné ???

Hey groupe !! Que ce passe-t-il ??? Il est temps que plusieurs d'entre vous redonnent de l'énergie au groupe! Lancez des discussions! battez-vous! Soyez énergiques!!

votre vieux boubou...

vendredi 9 janvier 2009

NING et le OpenSocial framework

http://telus-qc-veille-techno.ning.com/

J'ai créé un site sur NING pour nous permettre d'expérimenter avec cet outil.

Il nous permet de créer une communauté avec des blogues, des forums, des groupes et plein d'applications... vous pouvez créer vos propres application utilisant le OpenSocial Framework de Google.

vendredi 19 décembre 2008

BuildBot: Automatisation des constructions et des tests

Lors du développement d'un projet, il est toujours fastidieux de le construire et de le tester sur toutes les plate-formes qui doivent être supportées. C'est là que BuildBot vient à votre rescousse. C'est un outil, indépendant de la plate forme et du langage, permettant d'automatiser et de distribuer la construction et les tests pour un projet.

BuildBot est structuré de façon à ce que toute la configuration soit centralisée (BuildMaster) mais que les constructions se fassent sur des serveurs esclaves, qui sont les plates-formes à tester (BuildSlaves). Le serveur maître est lié à un dépôt SVN/CVS/etc. pour avoir accès aux changements qui sont faits dans le code source. Soit de façon programmée (ex: nightly builds), ou manuelle, le maître va demander aux esclaves d'exécuter un ensemble de scripts permettant de faire le travail voulu (construction, test, déploiement, etc.). Une fois le travail terminé par l'esclave, il va envoyer le rapport au maître qui pourra par la suite le diffuser via un serveur HTTP, par courriel, par IRC, etc.


Si vous êtes convaincu, BuildBot est disponible à l'adresse suivante: http://buildbot.net/

jeudi 18 décembre 2008

FireUnit: Une extension de test unitaire JavaScript!

// Some examples of using FireUnit
if ( typeof fireunit === "object" ) {
// Simple true-like/false-like testing
fireunit.ok( true, "I'm going to pass!" );
fireunit.ok( false, "I'm going to fail!" );

// Compare two strings - shows a diff of the
// results if they're different
fireunit.compare(
"The lazy fox jumped over the log.",
"The lazy brown fox jumped the log.",
"Are these two strings the same?"
);

// Compare a string using a regular expression
fireunit.reCompare(
"The .* fox jumped the log.",
"The lazy brown fox jumped the log.",
"Compare a string using a RegExp."
);

// Display the total results
fireunit.testDone();
}

FireUnit!