# Recherche de texte complet (Pt 1.1)

<!--category-- Postgres, Alpine -->
<datetime class="hidden">2024-08-21T20:30</datetime>

## Introduction

En ce qui concerne les [dernier article](/blog/textsearchingpt1) J’ai montré comment mettre en place une recherche de texte complet à l’aide des fonctionnalités intégrées de Postgres.

Pièces précédentes de cette série:

- [Recherche de texte complet avec Postgres](/blog/textsearchingpt1)

Autres pièces de cette série:

- [Introduction à OpenSearch](/blog/textsearchingpt2)
- [Recherche ouverte avec C#](/blog/textsearchingpt3)

Cela ajoutera une petite boîte de recherche au titre du site qui permettra aux utilisateurs de chercher le texte dans les articles de blog.

![Rechercher](searchbox.png?format=webp&quality=25)

**Remarque: L’éléphant dans la salle est que je ne considère pas comme le meilleur moyen de le faire . Pour appuyer les langues multilingues- la langage est super complexe M SK3IMSC4d besoin d’une colonne différente par langueMNK5 et IMRK6d doit traiter des questions liées à l’éloignement et d’autres choses spécifiques à une autre langue.**

[TOC]

## Recherche de texte

Pour ajouter une fonctionnalité de recherche, j’ai dû apporter quelques changements à la fonction api. de recherche. `EF.Functions.WebSearchToTsQuery("english", processedQuery)`

```csharp
    private async Task<List<(string Title, string Slug)>> GetSearchResultForQuery(string query)
    {
        var processedQuery = query;
        var posts = await context.BlogPosts
            .Include(x => x.Categories)
            .Include(x => x.LanguageEntity)
            .Where(x =>
                // Search using the precomputed SearchVector
                (x.SearchVector.Matches(EF.Functions.WebSearchToTsQuery("english", processedQuery)) // Use precomputed SearchVector for title and content
                || x.Categories.Any(c =>
                    EF.Functions.ToTsVector("english", c.Name)
                        .Matches(EF.Functions.WebSearchToTsQuery("english", processedQuery)))) // Search in categories
                && x.LanguageEntity.Name == "en")// Filter by language
            
            .OrderByDescending(x =>
                // Rank based on the precomputed SearchVector
                x.SearchVector.Rank(EF.Functions.WebSearchToTsQuery("english", processedQuery))) // Use precomputed SearchVector for ranking
            .Select(x => new { x.Title, x.Slug,  })
            .Take(5)
            .ToListAsync();
        return posts.Select(x=> (x.Title, x.Slug)).ToList();
    }
```

Cette option est utilisée lorsqu’il y a un espace dans la demande'.

```csharp
    if (!query.Contains(" "))
        {
            posts = await GetSearchResultForComplete(query);
        }
        else
        {
            posts = await GetSearchResultForQuery(query);
        }
```

Sinon, j'utilise la méthode de recherche existante qui ajoute le caractère préfixe.

```csharp
EF.Functions.ToTsQuery("english", query + ":*")

```

## Contrôle de la recherche

Utilisation [Alpine.js](https://alpinejs.dev/) J'ai fait un contrôle partiel simple qui fournit une boîte de recherche super simple.

```razor
<div x-data="window.mostlylucid.typeahead()" class="relative"    x-on:click.outside="results = []">

    <label class="input input-sm dark:bg-custom-dark-bg bg-white input-bordered flex items-center gap-2">
       
        
        <input
            type="text"
            x-model="query"

            x-on:input.debounce.300ms="search"
            x-on:keydown.down.prevent="moveDown"
            x-on:keydown.up.prevent="moveUp"
            x-on:keydown.enter.prevent="selectHighlighted"
            placeholder="Search..."
            class="border-0 grow  input-sm text-black dark:text-white bg-transparent w-full"/>
        <i class="bx bx-search"></i>
    </label>
    <!-- Dropdown -->
    <ul x-show="results.length > 0"
        class="absolute z-10 my-2 w-full bg-white dark:bg-custom-dark-bg border border-1 text-black dark:text-white border-b-neutral-600 dark:border-gray-300   rounded-lg shadow-lg">
        <template x-for="(result, index) in results" :key="result.slug">
            <li
                x-on:click="selectResult(result)"
                :class="{
                    'dark:bg-blue-dark bg-blue-light': index === highlightedIndex,
                    'dark:hover:bg-blue-dark hover:bg-blue-light': true
                }"
                class="cursor-pointer text-sm p-2 m-2"
                x-text="result.title"
            ></li>
        </template>
    </ul>
</div>
```

Cela a un certain nombre de classes CSS pour rendre correctement en mode foncé ou lumineux.. Le code Alpine.js est assez simple. . Il s’agit d’un simple contrôle typeahead qui appelle le api de recherche lorsque l’utilisateur type dans la case de recherche.
Nous avons également un petit code pour traiter le défocus pour fermer les résultats de recherche.

```html
   x-on:click.outside="results = []"
```

Notez que nous avons un déblocage ici pour éviter de frapper le serveur avec les demandes.

## Typeahead JS

Cela appelle à notre fonction JS (définie dans `src/js/main.js`)

```javascript
window.mostlylucid = window.mostlylucid || {};

window.mostlylucid.typeahead = function () {
    return {
        query: '',
        results: [],
        highlightedIndex: -1, // Tracks the currently highlighted index

        search() {
            if (this.query.length < 2) {
                this.results = [];
                this.highlightedIndex = -1;
                return;
            }

            fetch(`/api/search/${encodeURIComponent(this.query)}`)
                .then(response => response.json())
                .then(data => {
                    this.results = data;
                    this.highlightedIndex = -1; // Reset index on new search
                });
        },

        moveDown() {
            if (this.highlightedIndex < this.results.length - 1) {
                this.highlightedIndex++;
            }
        },

        moveUp() {
            if (this.highlightedIndex > 0) {
                this.highlightedIndex--;
            }
        },

        selectHighlighted() {
            if (this.highlightedIndex >= 0 && this.highlightedIndex < this.results.length) {
                this.selectResult(this.results[this.highlightedIndex]);
            }
        },

        selectResult(result) {
           window.location.href = result.url;
            this.results = []; // Clear the results
            this.highlightedIndex = -1; // Reset the highlighted index
        }
    }
}
```

Comme vous pouvez le voir, cela est assez simple (une grande partie de la complexité consiste à manipuler les touches ascendantes et descendantes pour sélectionner les résultats).
Ce poste est adressé à notre `SearchApi`
Lorsqu'un résultat est sélectionné, nous naviguons vers l'url du résultat.

```javascript
     search() {
            if (this.query.length < 2) {
                this.results = [];
                this.highlightedIndex = -1;
                return;
            }

            fetch(`/api/search/${encodeURIComponent(this.query)}`)
                .then(response => response.json())
                .then(data => {
                    this.results = data;
                    this.highlightedIndex = -1; // Reset index on new search
                });
        },
```

### HTMX

J'ai également modifié le téléchargement pour travailler avec HTMX, ce qui change simplement `search` méthode d'utilisation de HTMX refresh:

```javascript
    selectResult(result) {
    htmx.ajax('get', result.url, {
        target: '#contentcontainer',  // The container to update
        swap: 'innerHTML', // Replace the content inside the target
    }).then(function() {
        history.pushState(null, '', result.url); // Push the new url to the history
    });

    this.results = []; // Clear the results
    this.highlightedIndex = -1; // Reset the highlighted index
    this.query = ''; // Clear the query
}
```

Notez que nous échangeons le HTML interne du `contentcontainer` avec le résultat de la recherche. Il s'agit d'une façon simple de mettre à jour le contenu de la page en fonction du résultat de recherche sans que la page ne soit renouvelée.
Nous modifions également l'url dans l'histoire à la nouvelle url.

## En conclusion

Cela ajoute une capacité de recherche puissante mais simple au site. C’est un excellent moyen d’aider les utilisateurs à trouver ce qu’ils cherchent.
Il donne à ce site un sentiment plus professionnel et facilite la navigation.