Skip to content

Recherche & generation PDF

Recherche (Services/SearchService.cs)

  • ISearchService/SearchService (scoped) : recherche full-text sur les articles, qui proviennent du CMS headless.
  • Page de resultats : searchPage (vue SearchPage.cshtml).
  • Cote frontend : module JS scripts/modules/search.js.

Filtre par type & SEO de la page de resultats

  • Chaque SearchResultItem porte un ElementType (LINK = page locale, ARTICLE = article headless, MEDIA). La vue SearchPage.cshtml rend deux dropdowns Bootstrap (form-select, pattern repris de pully-website) : type (?type=article|link|media, option affichee seulement si le type a des resultats) et annee (?year=, affiche seulement si 2+ annees distinctes). Filtrage server-side dans la vue (le SearchWithPage est cache 2 min, donc rechargement bon marche) ; les selects auto-soumettent le form GET (search.jsinitResultFilters, classe .js-search-filter). Libelles via dictionnaire SearchPage.Filter.* (fallback FR en dur).
  • URL des resultats articles : les articles headless n'ont pas de nœud local — leur URL doit etre construite {url du BlogPage du site}/{slug} (routee par BlogContentFinder). SearchInArticles prend un siteRoot (le Root() de la search page) pour resoudre le BlogPage ; un slug brut serait resolu relativement a /recherche/ → 404 (bug corrige le 2026-07-16, idem dans mapBlock.cshtml ; favoriteBlock.cshtml avait deja le bon pattern).
  • Piege de config contenu : la propriete includeArticles (« Articles inclus », dropdown multiple) du nœud Recherche doit contenir les skins voulus (accm, icogne, linfo) — si elle est vide ou pointe le mauvais skin, SearchWithPage n'interroge pas du tout les articles headless (constate sur staging le 2026-07-15 : valeur ["icogne"] → aucun article ACCM dans la recherche).
  • SEO : les resultats de recherche ne doivent jamais etre indexes. Verrouille cote code a deux endroits : SearchPage.cshtml pose ViewBag.HideFromSitemap = true (les 4 layouts rendent alors <meta name="robots" content="noindex">) et PublishedContentExtensions.HideFromSiteMap() force true pour le doctype searchPage (exclusion du sitemap XML), independamment de ce que coche l'editeur.

Exclusion des conteneurs sans template

SearchInContent ne retourne que les nœuds qui possedent un template rendu (TemplateId > 0). Les doctypes de type conteneur (ex. pageContainer, « Conteneur de pages ») n'ont pas de template : ils servent uniquement a regrouper des enfants dans l'arbre. Leur Url() renvoie pourtant une URL valide via l'UrlProvider, mais la navigation aboutit a un 404 (pas de template a rendre). Sans le filtre TemplateId, ces conteneurs apparaissaient dans les resultats et generaient des 404 au clic. Garder ce filtre pour tout nouveau doctype non rendu.

Recherche full-text dans le corps des pages (fullTextContent)

Par defaut l'ExternalIndex n'indexe que nodeName, metaTitle, metaDescription : un mot present dans le corps d'une page (BlockGrid/BlockList/RTE) ne remontait pas. Examine/FullTextIndexHandler (branche sur TransformingIndexValues de l'ExternalIndex, enregistre dans SiteComposer) calcule un champ fullTextContent agregeant tout le texte extractible via Examine/FullTextExtractor (parcours du JSON des blocks, strip HTML, saut des GUID/Udi et des champs de plomberie). SearchInContent interroge ce champ avec un boost faible (^1 exact / ^0.5 fuzzy) pour ne jamais devancer un hit titre/meta.

IMPORTANT (operationnel) : le champ n'est peuple qu'a l'(re)indexation. Apres deploiement, faire un rebuild de l'ExternalIndex (backoffice → Settings → Examine Management → ExternalIndex → Rebuild), sinon fullTextContent reste vide et la recherche dans le corps ne remonte rien.

Analyzer : accents, apostrophes, separateurs de milliers

Examine/AccentAndSeparatorAnalyzer (enregistre via Examine/ExternalIndexAnalyzerConfigurator en IPostConfigureOptions<LuceneDirectoryIndexOptions> dans Program.cs) enveloppe l'analyzer de l'ExternalIndex. Comme Examine utilise le meme analyzer a l'indexation ET a la requete, la normalisation s'applique des deux cotes :

  • Char filters : variantes d'apostrophe (’‘ʼ) → ' ; separateur de milliers entre deux chiffres (apostrophe + espaces insecables/fines) supprime → 19'702 = 19 702 = 19702.
  • Token filter : ASCIIFoldingFilter → repli des diacritiques, donc fête = fete au niveau de l'index (insensibilite reelle aux accents, au-dela du simple fuzzy ~1).

Necessite aussi un rebuild de l'ExternalIndex pour s'appliquer au contenu existant.

Fuzzy et suggestion « vouliez-vous dire »

La requete principale utilise un fuzzy ~1 (mots ≥ 4 car.) pour tolerer une faute legere sans polluer les resultats. Quand une recherche ne renvoie aucun resultat, SearchWithPage relance une passe fuzzy agressif (~2 pour 5+ car., ~1 pour 4 car., via BuildAggressiveFuzzyTerm) sur le contenu + les medias — uniquement pour constituer un pool de candidats et proposer une correction (BuildSuggestion / SearchTextHelper.BuildSuggestedQuery). Ces hits agressifs ne sont pas affiches comme resultats. Les articles headless sont exclus de cette passe (pas d'index Examine a « fuzzer »).

Portage inspire de pully-website (SearchService, FullTextIndexHandler, ThousandsSeparatorAnalyzer), adapte a ACCM (non culture-variant, ajout du folding d'accents).

Generation PDF (Services/PdfGeneratorService.cs)

  • IPdfGeneratorService/PdfGeneratorService (scoped).
  • Stack : itext 9.3 pour le rendu PDF + Fluid (templates Liquid) pour le contenu.
  • Usage : generation de documents PDF a partir de contenu/templates.

Ces deux services sont enregistres dans Program.cs.

Contributors

No contributors

Changelog

No recent changes