Algolia et l'utilisation de instantsearch.js à partir de votre script pour Shopify

"Les bons artistes copient, les grands artistes volent." - Steve Jobs

Inspiré par pimoroni.com Je voulais mettre en place une recherche similaire sur notre boutique en ligne, buyzero.de buyzero.de est actuellement géré par Shopify.

La recherche affiche les résultats au fur et à mesure que vous tapez - dans la page principale, remplaçant dynamiquement le contenu affiché précédemment. Très cool, très utile - et très, très rapide (merci à Algolia!)

Comme je me suis débattu avec la documentation disponible sur Algolia et que leur service d'accueil n'a pas été d'une grande aide, j'ai dû me débrouiller tout seul et faire de nombreuses recherches sur Google.

Voici un résumé de mon expérience de mise en œuvre d'Algolia. Je l'ai fait en un seul jour, en une seule session d'environ 12 à 14 heures de codage.

Préparation suggérée : injection de code de développement dans votre page

Je suggère d'utiliser l'extension Google Chrome Tampermonkey pour injecter du CSS et du Javascript depuis votre disque dur local pendant que vous développez.

Pour ce faire, vous devez activer l'accès aux fichiers pour Tampermonkey. Ouvrez :

chrome://extensions

Cliquez sur Détails et activez l'option "Autoriser l'accès aux URL des fichiers".

image

image

Créez le nouveau script utilisateur suivant dans Tampermonkey :

// ==UserScript==
// @name Algolia-Inject
// @namespace https://pi3g.com/
// Version 0.5
// @description injecter un fichier local pour tester Algolia !
// @author Maximilian Batz
// @grant GM_getResourceText
// @grant GM_addStyle
// @noframes
// @include https://buyzero.de/*
// @resource YOUR_CSS file://D:\algolia_jr.css
// @require file://D:\algolia_jr.js

// ==/UserScript==

var my_css = GM_getResourceText ("YOUR_CSS") ;
GM_addStyle(my_css) ;

Explication :

  • vous devez accorder getResourceText et addStyle pour pouvoir accéder et injecter le fichier CSS
  • noframes pour ne pas s'exécuter une seconde fois dans les cadres que Shopify charge apparemment, et qui correspondrait également au domaine
  • include -> correspondra à buyzero.de. Vous pourriez vouloir changer cette ligne Sourire
  • ressource utilisée pour importer le fichier CSS. Indiquez le chemin d'accès ... sous Linux, ce pourrait être file:///home/whatever/whatever - remarquez la triple barre oblique
  • require - inclure votre fichier javascript
  • userscript : c'est le userscript pour ajouter le fichier CSS

Ajout du code Algolia

Maintenant que la partie "comment développer" est réglée, voici mon code en tant qu'exemple de code dans un joli petit fichier ZIP.

Veuillez ne pas faire de copier-coller à partir de ce blog, car je ne suis pas sûr que WordPress détruira le formatage du code ... utilisez le .js dans le fichier ZIP pour copier.

Veuillez noter que les dépendances Algolia / les fichiers JavaScript étaient déjà installés dans notre thème Shopify - en installant le plugin Algolia Shopify. Notre datastore était également configuré, etc. Algolia proposait déjà (et propose toujours) des résultats de recherche dans une liste déroulante lorsque vous tapez dans le champ de recherche.

Je vais vous guider à travers les parties importantes du code :

nous lançons aj_setup() une fois que le document est prêt, pour injecter notre nouvelle fonctionnalité de recherche.

Veuillez noter que j'utilise aj pour précéder mes fonctions et id's pour ce script. Vous pouvez utiliser ce que vous voulez, ce n'est pas requis par Algolia.

Le conteneur

Dans aj_setup(), j'ai créé un nouveau conteneur pour les résultats de la recherche (il serait probablement préférable d'inclure ceci directement dans votre fichier de thème - pour le développement, c'était plus facile).

Voici la partie HTML de la recherche

$(".main-content").before('<div id="aj-search-container" class="aj-main-content full-width"><div class="background"><div class="pattern"><div class="container"><div class="row"><div class="col-md-12"><div class="row"><div class="col-md-12 center-column content-without-background">'+
                         "
"+
                           "<h1>Ces résultats sont pour : <span id='sc-query'></span></h1>" +
                           "
" +
                             "
" +
                             "
" +
                             "
" +
                             "
" +
                           "
" +
                           "
" +
                               "
" +
                               "
" +
                               "
" +
                           "</DIV" +
                       "
" +
                       '</div></div></div></div></div></div></div></div>'
                       );
  • Je lui donne l'identité aj-search-container, pouvoir afficher / masquer l'ensemble des résultats du conteneur de recherche
  • la plupart des div's, dont conteneur sont nécessaires pour un style compatible avec notre thème actuel - utilisez-les si nécessaire.
  • sc-query est utilisé pour refléter la recherche que l'utilisateur est en train de saisir, afin de montrer que le système met à jour les résultats de manière dynamique en son nom (il y a du code pour faire cela - lisez la suite).
  • aj-right-container contient des filtres pour obtenir des "facettes" de la recherche. L'utilisateur peut choisir une ou plusieurs marques à afficher, des catégories de produits, et nous incluons un filtre de prix.
  • aj-clear-all contiendra un lien pour effacer les facettes - pour retourner tous les résultats qui correspondent à la requête de l'utilisateur.
  • aj-left-container contient les résultats de la recherche. aj-stats contient des statistiques sur la recherche (résultats de la recherche + temps de recherche), aj-search-results contient les résultats et aj-search-pagination contient la pagination.

Le modèle de frappe

Encore une fois, il est probable que cela soit intégré directement dans votre page au lieu d'être injecté via Javascript.

    $(".main-content").before(''+
       ''+
'          <div class="aj-hit">'+
'            <div class="aj-hit-image">'+
'              <a href="/fr/{{product_link}}/"><img src="{{image}}" alt="{titre}}"></a>'+
'            </div>'+
'            <div class="aj-hit-content">'+
'              <div class="aj-hit-title"><a href="/fr/{{product_link}}/">{{{_highlightResult.title.value}}}{{variant_title}}</a></div>'+
               '<p class="aj-hit-description">{{{_highlightResult.body_html_safe.value}}} &nbsp; <a href="/fr/{{product_link}}/">" mehr</a></p>'+
               '<p class="aj-hit-sku"><a href="/fr/{{product_link}}/">{sku}}</a></p>'+
'              <div class="aj-hit-bottominfos">' +
'                <div class="aj-hit-price">{{prix}} €</div>' +
'                <div class="aj-hit-stock">{{inventory_quantity}} auf Lager </div>'+
'                <div  class="aj-hit-link"><a href="/fr/{{product_link}}/">sur la page des produits</a></div>'+
'             </div>'+
             '</div>'+
             '
' +
           '</div>'+
         ''+
     '');

Ce modèle est inséré comme "script". C'est la méthode recommandée par la documentation d'Algolia.

Il est utilisé pour rendre un résultat de recherche, ou hit.

Ici, à l'intérieur aj-hit nous avons :

  • aj-hit-imageen utilisant {{lien_produit}} et {{image}} comme espaces réservés pour insérer les valeurs correctes ultérieurement.
  • aj-hit-titleJ'utilise {{{_highlightResult.title.value}} - Je crée également un lien vers {{variant_title}} pour fournir un titre complet aux variantes.
  • aj-hit-descriptionpour rendre la description du produit - {{{_highlightResult.body_html_safe.value}}}.
  • aj-hit-price pour le {{prix}} - n'oubliez pas d'ajouter votre devise (ou de l'ajouter en modifiant la variable passée au modèle).
  • aj-hit-stockpour montrer combien de {{inventory_quantity}} nous avons
  • aj-hit-link - enfin, un lien vers la page du produit. {{lien_produit}}

Tout ceci est joliment (IMHO) stylé avec mon fichier CSS. Le stylisme n'entre pas dans le cadre de ce billet de blog. buyzero.de pour vous inspirer et coder le vôtre.

Plus sur {{{_highlightResult.title.value}}}

Les variables affichées à l'aide de trois accolades ne seront pas échappées par Algolia pour HTML. Ceci est utilisé ici, pour permettre la mise en évidence.

Algolia placera automatiquement des balises autour des parties du titre, etc. qui ont été trouvées dans la base de données. _highlightResult variable. Vous pouvez utiliser le dans la sortie, pour montrer à l'utilisateur pourquoi le résultat de la recherche est considéré comme pertinent pour sa recherche.

Je vous montrerai plus tard comment modifier les valeurs qui sont transmises au modèle, car vous ne voudrez pas rendre l'intégralité du corps de la description de votre produit, par exemple.

aj_setup_search

var pb = aj_setup_search() ;

Pour gérer la recherche plus tard, nous retournons une variable à partir de la fonction setup (appelée var point_both dans la fonction setup_search. pb est son abréviation).

	
var search = instantsearch({
	  appId: 'YOUR_APP_ID',
	  apiKey: 'YOUR_API_KEY', // search only API key, no ADMIN key
	  indexName: 'shopify_buyzero_de_products',
	  urlSync: true,
	  searchParameters: {
		hitsPerPage: 10
	  },
	  searchFunction(my_helper){
		  point_both.ihelper = my_helper;
		  //search.helper.setQuery('Pi 3'); //sic!
		  //my_helper.search(); //sic!
		  
		  if(my_helper.state.query === '') {	
				return;
			}
		  my_helper.search();
	  } //end searchFunction
	});//end instantsearch setup

Ici, nous configurons la recherche (comme mentionné précédemment, instantsearch est déjà inclus et disponible sur la page via un autre fichier .js).

Vous devez passer :

  • votre identifiant d'application
  • apiKey
  • Nom de l'indice

vous les obtiendrez à partir de votre backend Algolia. Vous pouvez également y parcourir vos structures de données, et voir quels paramètres vous pouvez utiliser pour les facettes, par exemple.

La partie vraiment importante est la fonction searchFunction (my_helper).

L'aide vous permet de piloter la recherche via votre propre script, au lieu de la déclencher à partir d'un widget de boîte de recherche qui est configuré avec instantsearch d'Algolia.

Il est important de créer une référence à cette aide, dans cette fonction particulière.

point_both.ihelper = my_helper ;

L'accès ultérieur à l'aide à partir de la variable de recherche permettra de pas travail.

En fait, vous avez besoin des deux. Tu vois cet exemple de code, que j'ai commenté ?

//search.helper.setQuery('Pi 3') ; //sic !

//my_helper.search() ; //sic !

sic est le mot latin pour "comme ceci". Cela signifie que l'on doit y accéder de deux manières différentes pour définir la requête et exécuter la recherche.

Aussi, vous besoin de pour ajouter my_helper.search() ici. Cela permettra - si je m'en souviens bien - à vos widgets de mettre à jour la recherche correctement ... si vous trouvez que vos résultats ne sont pas mis à jour, vérifiez si vous l'avez mis là !

Après avoir ajouté les widgets - dont nous parlerons plus loin - nous terminons la configuration de la recherche avec le code suivant :

recherche.start() ;
point_both.search = search ;
Retourner le point_d'origine ;

Ici, nous ajoutons également une référence à l'objet de recherche lui-même, et nous le renvoyons à la fonction aj_setup.

Notez que point_both a maintenant accès à l'objet "search" et à l'objet d'aide spécial "ihelper" que nous avons obtenu dans la fonction searchFunction.

Ajout du premier widget

nous ajouterons le résultat comme premier widget :

search.addWidget(
   instantsearch.widgets.hits({
     conteneur : '#aj-search-results',
     les modèles : {
       item : document.getElementById('aj-hit-template').innerHTML,
       empty : "Es konnten leider keine Resultate für die Suchanfrage \"{{query}}\" gefunden werden."
     },
     transformData : {
         item : function (my_result) {
         //console.log(mon_résultat) ;
         //console.log(mon_résultat._highlightResult.body_html_safe.value) ;
         si (my_result["variant_title"] == 'Default Title'){
             my_result["variant_title"] = " ;
         }
         sinon
         {
             my_result["variant_title"] = ' : : ' + my_result["variant_title"] ;
         }
         //my_result["body_html_safe"] = my_result["body_html_safe"].substring(0,175) + '...' ;
         //nous ne voulons PAS de ligne multiple, nous voulons correspondre à la VRAIE fin de la chaîne. /m correspondrait à la fin de la ligne ...
         var pattern_a = /(.* ?)(]*)?$/i ;
         var pattern_b = /[^<]+$/i ;
        
         //prendre le premier groupe correspondant - ce qui exclut le deuxième groupe correspondant.
         var body_string = my_result._highlightResult.body_html_safe.value.substring(0,175).replace(pattern_a, "$1″) ;
        
         si (pattern_b.test(body_string)){
             body_string = body_string + "
"
         }
         //Recherche de pi 3 b+
         //identification du produit https://buyzero.de/products/budget-kit-raspberry-pi-3-model-b?variant=698066665499
         my_result._highlightResult.body_html_safe.value = body_string + '...' ;
        
         my_result["product_link"] = "https://buyzero.de/products/” + my_result["handle"] + "?variant=" + my_result["id"] ;
         retourner mon_résultat ;
         }
     }
   })
);

Le conteneur pour mettre la sortie est sélectionné comme '#aj-search-results', le modèle pour rendre les résultats est sélectionné ici : item : document.getElementById('aj-hit-template').innerHTML.

Je suppose qu'ils ont mis cela dans une balise , car le contenu n'est pas rendu en tant que HTML par le navigateur.

Pour le vide, nous renvoyons simplement une chaîne de caractères comme modèle.

C'est très simple jusqu'à présent, non ?

Ensuite, nous utilisons transformData pour préparer une version abrégée du corps de texte de votre produit et d'autres variables transmises au modèle de résultats discuté précédemment.

Comme nous avons introduit des dans le résultat de la recherche - en utilisant la mise en évidence automatique d'Algolia - nous devons raccourcir le texte d'une manière sûre. J'ai préparé mes propres expressions régulières pour cela, dans mon code d'exemple.

S'il y a une déclaration incomplète commençant par correspondant), elle est coupée de la sortie.

S'il existe une balise sans balise correspondante pour la fermer, nous l'ajoutons.

Le lien vers le produit est construit en obtenant la poignée, et en définissant l'id comme variante.

Ajout de la pagination

L'ajout de widgets supplémentaires est facile :

// Ajoutez ceci après les autres appels à search.addWidget().
search.addWidget(
   instantsearch.widgets.pagination({
     conteneur : '#aj-search-pagination',
     render : function(my_obj){
         console.log("render") ;
         console.log(mon_obj) ;
     },
     getConfiguration : function(my_obj){
         console.log("getConfiguration") ;
         console.log(mon_obj) ;
     }
   })
);

vous ne voulez pas la sortie du journal de la console ... Je l'utilisais pour déboguer, et j'ai apparemment oublié de l'enlever. Désolé. Je suis un humain.

Là encore, le conteneur de sortie du widget est défini. #aj-recherche-pagination

Vous n'avez pas vraiment besoin d'autre chose pour que la pagination fonctionne - configurez le widget, et donnez-lui un style - vous serez prêt.

Facettes

Grâce aux facettes, votre utilisateur peut affiner sa recherche :

// Facettes
search.addWidget(
   instantsearch.widgets.refinementList({
     conteneur : '#aj-facet-brand',
     nom de l'attribut : "vendor",
     opérateur : "ou",
     limite : 5,
     showMore : true,
     cssClasses : {
         header : 'aj-refine-header',
         compte : aj-refine-count
     },
     les modèles : {
       en-tête : "Marke / Marque".
     }
   })
);

  • vous devez regarder les noms d'attributs possibles dans votre backend Algolia - cela vous montrera les facettes disponibles pour vos données (voir la capture d'écran ci-dessous).
  • La limite de 5 ne se réfère pas aux résultats de votre recherche, mais à la limite des entrées qui sont affichées par Algolia pour votre liste de raffinement.
  • Les affinements disponibles seront automatiquement mis à jour si les autres paramètres de recherche sont modifiés (par exemple, si aucune correspondance n'est trouvée pour une marque spécifique, cette marque ne sera pas affichée).
  • showMore : affiche un lien "show more" au bas du widget.
  • compte : aj-refine-count' - vous pouvez styliser un peu vos résultats de recherche. Probablement plus que ce que j'ai utilisé ici - se référer à la documentation officielle

image

Le widget Clear Facets

search.addWidget(
   instantsearch.widgets.clearAll({
     conteneur : '#aj-clear-all',
     les modèles : {
       lien : 'Filter zurücksetzen / Reset' (filtre à réinitialiser)
     },
     autoHideContainer : false,
     ClearsQuery : true,
   })
);

utilisez cette option pour permettre au client d'effacer les facettes (c'est-à-dire que tous les résultats correspondant à sa requête lui seront à nouveau présentés).

Il n'est pas nécessaire de configurer en plus.

Je ne parlerai pas des autres widgets - jetez un coup d'œil à mon code si vous êtes intéressé.

Liaison avec le champ de recherche

Nous n'utilisons pas le widget de recherche d'Algolia, mais nous nous lions à un événement keyup sur l'entrée de notre formulaire de recherche, comme ceci :

    var pb = aj_setup_search() ;
     //#search_query est l'entrée que nous voulons.
     $("#search_query").keyup(function(search) {
       mettre à jour la recherche(pb) ;
     });   
}

(nous venons de discuter de aj_setup_search, il est ajouté ici pour vous montrer d'où vient pb).

Cela nous permet d'effectuer un traitement supplémentaire lorsque l'événement keyup est déclenché : masquer le contenu que nous affichions précédemment sur la page, ajouter les résultats de la recherche, etc.

function updateSearch(pb){
     var my_query = $("#search_query").val() ;
     //$(".main-content").html(my_query) ;
     if (my_query) {
         $(".main-content").hide() ;
         $("#megamenu-header-menu2").hide() ;
         $("#ajr_na").show() ;
         $("#sc-query").html(ma_query) ;
         $("#aj-search-container").show() ;
         pb.search.helper.setQuery(my_query) ;
         //console.log(search_handle.helper.state) ;
         pb.ihelper.search() ;
     }
     else {
         $("#aj-search-container").hide() ;
         $(".main-content").show() ;
         $("#ajr_na").hide() ;
         $("#megamenu-header-menu2").show() ;
     }

}

Nous utilisons jQuery pour obtenir la valeur du champ de recherche. S'il n'est pas vide, nous cachons le contenu principal que nous affichions auparavant et affichons le conteneur de recherche.

Nous définissons sc-query pour montrer à l'utilisateur que nous répondons à son entrée : $("#sc-query").html(my_query) ;

Enfin, les éléments importants sont ceux-là :

pb.search.helper.setQuery(my_query) ;
pb.ihelper.search() ;

comme vous le voyez, Nous devons utiliser deux aides différentes pour définir la requête et exécuter la recherche proprement dite.. Oui, c'est ainsi qu'il faut procéder - heureusement, j'ai trouvé quelqu'un qui se plaignait de ce comportement.

Enfin, si la chaîne de recherche est vide (l'utilisateur a appuyé sur Echap, ou l'a supprimée, ou nous l'avons supprimée, ou autre...), nous masquons la recherche et affichons le contenu principal.

Merci de votre lecture

Merci pour votre attention, j'espère que cela aidera quelqu'un qui souhaite mettre en œuvre instantsearch.js sans utiliser le widget de recherche d'Algolia.

Références

Tampermonkey

Algolia

Bonus