Ajout possibilité ou non de rendre le moteur de recherche sensible à la casse, aux accents ou encore à ignorer ou pas certains caractères spéciaux.

This commit is contained in:
Fabrice PENHOËT 2022-02-21 15:51:53 +01:00
parent ce1356f10a
commit a7ca9c2e1a
4 changed files with 90 additions and 11 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "freedatas2html", "name": "freedatas2html",
"version": "1.3.2", "version": "1.4.0",
"description": "Conversion and display of data in different formats (CSV, JSON, HTML) with the possibility of filtering, classifying and paginating the results.", "description": "Conversion and display of data in different formats (CSV, JSON, HTML) with the possibility of filtering, classifying and paginating the results.",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -1,5 +1,5 @@
const errors=require("./errors.js"); const errors=require("./errors.js");
import { DOMElement, Filters } from "./interfaces"; import { DOMElement, Filters, SearchModeSettings } from "./interfaces";
import { FreeDatas2HTML } from "./FreeDatas2HTML"; import { FreeDatas2HTML } from "./FreeDatas2HTML";
export class SearchEngine implements Filters export class SearchEngine implements Filters
@ -13,6 +13,13 @@ export class SearchEngine implements Filters
public placeholder: string=""; public placeholder: string="";
public automaticSearch: boolean=false; public automaticSearch: boolean=false;
private _inputValue: string=""; private _inputValue: string="";
public searchMode:SearchModeSettings= // par défaut, recherche lâche, mais peut devenir stricte en passant tout à false
{
accentOff: true,
caseOff: true,
specialCharsOff: true,
specialCharsWhiteList: "",
}
// Injection de la classe principale, mais uniquement si des données ont été importées // Injection de la classe principale, mais uniquement si des données ont été importées
constructor(converter: FreeDatas2HTML, elt: DOMElement, fields?: number[]) constructor(converter: FreeDatas2HTML, elt: DOMElement, fields?: number[])
@ -110,10 +117,34 @@ export class SearchEngine implements Filters
mySearch._converter.refreshView(); mySearch._converter.refreshView();
}); });
} }
// Pré-traitement des chaînes de caractères à comparer, suivant le mode de recherche
private searchPreProcessing(searchElement: string) : string
{
let finalString=searchElement;
if(this.searchMode.accentOff) // caractères accentués remplacés (exemple : "é" -> "e")
finalString=finalString.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); // cf. https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript
if(this.searchMode.caseOff)
finalString=finalString.toLowerCase();
if(this.searchMode.specialCharsOff)
{
// Suppression de tous les caractères "spéciaux", c'est-à-dire n'étant ni une lettre, ni un chiffre
// ! Doit être exécuté après "accentOff", sans quoi les caractères accentués seront supprimés avant d'être remplacés
const validChars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"+this.searchMode.specialCharsWhiteList;
let validString="";
for(const letter of finalString)
{
if(validChars.indexOf(letter) !== -1)
validString+=letter;
}
finalString=validString;
}
return finalString;
}
public dataIsOk(data: {[index: string]:string}) : boolean public dataIsOk(data: {[index: string]:string}) : boolean
{ {
const realSearch=this._inputValue.trim().toLowerCase(); const realSearch=this.searchPreProcessing(this._inputValue.trim());
// Pas de valeur sélectionnée = pas de filtre sur ce champ // Pas de valeur sélectionnée = pas de filtre sur ce champ
if(realSearch.length === 0) if(realSearch.length === 0)
return true; return true;
@ -122,8 +153,7 @@ export class SearchEngine implements Filters
{ {
if(this._fields2Search.indexOf(field) !== -1) if(this._fields2Search.indexOf(field) !== -1)
{ {
// Attention, recherche insensible à la casse, mais aux accents, etc. if(this.searchPreProcessing(data[field]).indexOf(realSearch) !== -1)
if(data[field].toLowerCase().indexOf(realSearch) !== -1)
return true; return true;
} }
} }

View File

@ -81,6 +81,13 @@ export interface RemoteSources extends RemoteSourceSettings
{ {
getFetchSettings() : {}; getFetchSettings() : {};
} }
export interface SearchModeSettings
{
accentOff: boolean;
caseOff: boolean;
specialCharsOff: boolean;
specialCharsWhiteList: string;
}
export interface Selectors extends Filters export interface Selectors extends Filters
{ {
datasFieldNb: number; datasFieldNb: number;

View File

@ -239,9 +239,9 @@ describe("Test du moteur de recherche.", () =>
it("Doit retourner false, si une donnée testée ne correspond pas à la valeur cherchée.", async () => it("Doit retourner false, si une donnée testée ne correspond pas à la valeur cherchée.", async () =>
{ {
searchInput.value="Halogène"; searchInput.value="Hallogène";
searchInput.dispatchEvent(new Event("input")); searchInput.dispatchEvent(new Event("input"));
expect(mySearch.dataIsOk({ "Famille": "Halogene" })).toBeFalse();// sensible aux accents expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeFalse();
}); });
it("Doit retourner true, si la valeur recherchée est trouvée dans la donnée recherchée, sans prendre en compte la casse, ni les espaces entourant la saisie.", () => it("Doit retourner true, si la valeur recherchée est trouvée dans la donnée recherchée, sans prendre en compte la casse, ni les espaces entourant la saisie.", () =>
@ -254,15 +254,57 @@ describe("Test du moteur de recherche.", () =>
searchInput.value="gène"; searchInput.value="gène";
searchInput.dispatchEvent(new Event("input")); searchInput.dispatchEvent(new Event("input"));
expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue(); expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue();
// Insensible à casse :
searchInput.value="halo";
searchInput.dispatchEvent(new Event("input"));
expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue();
// Espace entourant la saisie ignorés : // Espace entourant la saisie ignorés :
searchInput.value=" halo "; searchInput.value=" halo ";
searchInput.dispatchEvent(new Event("input")); searchInput.dispatchEvent(new Event("input"));
expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue(); expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue();
// Par défaut, la recherche doit être tolérante à la casse, à la présence ou non d'accent et ignorer les caractères n'étant ni des lettres, ni des chiffres
searchInput.value="Halogene";
searchInput.dispatchEvent(new Event("input"));
expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue();
searchInput.value="halogène";
searchInput.dispatchEvent(new Event("input"));
expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue();
searchInput.value="#Halogène";
searchInput.dispatchEvent(new Event("input"));
expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue();
}); });
it("Si demandé doit traiter les données avant de les comparer de manière à prendre en compte les accents, majuscules ou caractères spéciaux.", () =>
{
// Sensible à casse :
mySearch.searchMode.caseOff=false;
searchInput.value="halogène";
searchInput.dispatchEvent(new Event("input"));
expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeFalse();
searchInput.value="Halogène";
searchInput.dispatchEvent(new Event("input"));
expect(mySearch.dataIsOk({ "Famille": "halogène" })).toBeFalse();
// Sensible aux accents :
mySearch.searchMode.accentOff=false;
searchInput.value="Halogene";
searchInput.dispatchEvent(new Event("input"));
expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeFalse();
searchInput.value="Halogène";
searchInput.dispatchEvent(new Event("input"));
expect(mySearch.dataIsOk({ "Famille": "Halogene" })).toBeFalse();
// Prise en compte des caractères spéciaux :
mySearch.searchMode.specialCharsOff=false;
searchInput.value="Halogène^";
searchInput.dispatchEvent(new Event("input"));
expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeFalse();
searchInput.value="Halogène";
searchInput.dispatchEvent(new Event("input"));
expect(mySearch.dataIsOk({ "Famille": "Ha+logène" })).toBeFalse();
// Ignore les caractères spéciaux, sauf ceux en liste blanche :
mySearch.searchMode.specialCharsOff=true;
mySearch.searchMode.specialCharsWhiteList="^+";
expect(mySearch.dataIsOk({ "Famille": "Ha+logène" })).toBeFalse();
searchInput.value="Halogène^";
searchInput.dispatchEvent(new Event("input"));
expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeFalse();
});
}); });
}); });