Champ de recherche
On voit maintenant très souvent des applications intégrant des possibilités de recherche. Il existe plusieurs possibilités:
- Effectuer des recherches via des requêtes directes en base de données.
- Utiliser un serveur de recherche (indexation et requête texte) dédié comme solr ou elasticsearch.
- Écrire les requêtes de recherche.
- Accéder à une liste filtrée par un terme de recherche.
Sans indexation, un champ de recherche se limite à une requête qui peut être plus ou moins complexe. L'opérateur qui nous servira le plus, c'est LIKE qui nous permet une forme de motifs (une version très allégée d'expressions régulières). On a accès à deux caractères spéciaux _ et %, le premier désigne un caractère quelconque, le second une suite de caractères quelconque.
Quelques exemples:
- "%tion%" désignera toutes les valeurs qui contiennent la syllabe "tion"
- "%mot%" désignera toutes les chaînes contenant le mot, ce qui nous servira pour nos recherches pour un mot clé.
- "#FF__FF" désignera toutes les chaînes de caractères ressemblant à des couleurs RGB avec le rouge et le bleu au maximum, peu importe le vert.
- "_t%" désignera toutes les chaînes dont la 2ème lettre est un "t"
Requête statique, un mot
Nous sommes dans la situation où on cherche un mot, soit dans le nom du restaurant soit dans le nom d'un plat, et on souhaite récupérer les restaurants. La requête fonctionne avec un seul mot clé.
public static List<Restaurant> search(String search) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("RestoPresto");
EntityManager em = emf.createEntityManager();
String word = search.toLowerCase();
String searchQuery =
"select distinct r from Restaurant r, IN(r.foodItems) f"+
" where lower(trim(r.name)) like lower(trim(:word))"+
" or lower(trim(f.name)) like lower(trim(:word)) ";
System.out.println(searchQuery);
Query q = em.createQuery(searchQuery, Restaurant.class);
q.setParameter("word", "%" + word + "%");
return q.getResultList();
}
Requête dynamique, plusieurs mots
Sans savoir combien de mots existent dans la chaîne de recherche, il est impossible d'écrire une requête statique. Nous devons donc écrire une requête dynamique qui contiendra autant de prédicats que de mots. Pour illustrer le fonctionnement nous prendrons le OU (disjonction) de ces prédicats, autrement dit un restaurant sortira s'il contient un mot ou un autre.
public static List<Restaurant> searchMulti(String search) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("RestoPresto");
EntityManager em = emf.createEntityManager();
// get all search key words
String[] words = search.trim().split(" ");
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Restaurant> query = cb.createQuery(Restaurant.class);
Root<Restaurant> root = query.from(Restaurant.class);
List<Predicate> conditions = new ArrayList<Predicate>();
Path<String> name = root.<String>get("name");
// for each word in string
for (String word: words){
conditions.add(cb.like(cb.lower(name),"%" + word.toLowerCase() + "%"));
}
Predicate or = cb.or(conditions.toArray(new Predicate[conditions.size()]));
query.where(or);
TypedQuery<Restaurant> tq = em.createQuery(query);
List<Restaurant> restos = tq.getResultList();
return restos;
}
Fichiers du projet
Le projet avec nos deux options de recherche se trouve ici. La recherche se fait à partir de la liste de restaurants.
Plus loin sur la recherche
Nos requêtes ne permettent pas de renvoyer des résultats proches ("restaurant" pour "retaurant" etc.). Elles doivent être écrite à la main etc. et peuvent poser des problèmes de performance.
C'est la raison pour laquelle des librairies comme Lucene et des serveurs comme Solr ou elasticsearch existent. Ces composants permettent d'indexer efficacement des informations et d'avoir une réelle souplesse dans la recherche de données.
Aucun commentaire:
Enregistrer un commentaire