# Cairn academic book search # Selected source by Kévin Feltrin, February 2026. # ESClient.search from webapp/utils/es_client.py, lines 29–99. # Original project comments are in French. # Revision 9791c162f635162ba3912e4150694c6748088312. def search( self, query: str = "", filters: Optional[dict[str, list[str]]] = None, page: int = 1, size: int = 20, ) -> dict: """ Recherche des ouvrages avec filtres et pagination. Args: query: Texte de recherche (titre, description, auteurs) filters: Dictionnaire de filtres par facettes ex: {"theme": ["SHS"], "editeur": ["PUF"]} page: Numéro de page (commence à 1) size: Nombre de résultats par page Returns: Dictionnaire avec 'total' et 'hits' (liste de documents) """ # Calcul de l'offset pour la pagination from_offset = (page - 1) * size # Construction de la query must_clauses = [] filter_clauses = [] # Recherche textuelle si une query est fournie if query: must_clauses.append({ "multi_match": { "query": query, "fields": ["title^3", "subtitle^2", "description", "authors"], "type": "best_fields", "fuzziness": "AUTO" } }) # Ajout des filtres par facettes if filters: for field, values in filters.items(): if values: # Seulement si la liste n'est pas vide filter_clauses.append({ "terms": {field: values} }) # Construction du body de la requête body = { "from": from_offset, "size": size, "query": { "bool": { "must": must_clauses if must_clauses else [{"match_all": {}}], "filter": filter_clauses } }, "sort": [ {"_score": {"order": "desc"}}, {"date_parution": {"order": "desc"}} ] } try: response = self.es.search(index=self.index, body=body) return { "total": response["hits"]["total"]["value"], "hits": response["hits"]["hits"] } except Exception as e: print(f"Erreur lors de la recherche : {e}") return {"total": 0, "hits": []}