This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Recherche de texte complet (Pt 1.1)
Wednesday, 21 August 2024
Introduction
En ce qui concerne les dernier article 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:
Autres pièces de cette série:
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.

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.
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)
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'.
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.
EF.Functions.ToTsQuery("english", query + ":*")
Contrôle de la recherche
Utilisation Alpine.js J'ai fait un contrôle partiel simple qui fournit une boîte de recherche super simple.
<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.
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)
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.
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:
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.