Vol tekst zoeken (Pt 1.1)
Inleiding
In de laatste artikel Ik liet jullie zien hoe je een volledige tekst zoekopstelling opstelt met de ingebouwde volledige tekst-zoekcapaciteiten van Postgres. Terwijl ik een search api blootlegde, had ik geen manier om het te gebruiken.
Vorige onderdelen in deze reeks:
Volgende onderdelen in deze reeks:
Dit voegt een kleine zoekbox toe aan de opskrif van de site waardoor gebruikers kunnen zoeken naar tekst in de blogposts.

Let op: De olifant in de zaal is dat ik het beste manier niet beschouw om dit te doen . Multilinguïsme te ondersteunen is supercomplicerend (IM SK4 heb je per taal een andere kolom nodigMska5 en IMske6 heb ik 'stemming' en andere taalspecifieke dingen moeten aanpakkenM Ska7 IkMskka8 ga dit nu negeren en focussen alleen op Engels Mska9 Alsjeblieft laten we zien hoe we dit in OpenSearch kunnen aanpakken.
Op zoek naar tekst
Om een zoekfunctie toe te voegen moest ik wat veranderingen aan de search api. doen. 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();
}
Dit wordt opsionele gebruikt als er een ruimte is in de zoekopdracht.
if (!query.Contains(" "))
{
posts = await GetSearchResultForComplete(query);
}
else
{
posts = await GetSearchResultForQuery(query);
}
Anders gebruik ik de bestaande zoekmethode die de voorvoegsel-gedrag toevoegt.
EF.Functions.ToTsQuery("english", query + ":*")
Soektog-controle
Met Alpine.js Ik maakte een simpele gedeeltelijke controle die een super eenvoudige zoekbox biedt.
<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>
Dit heeft een heleboel CSS-klassen om goed te weergeven voor donker of licht.. De Alpine code is vrij simpel. We hebben ook een kleine code om niet te focussen om de zoekresultaten af te sluiten.
x-on:click.outside="results = []"
Let op dat we hier een debounce hebben om de server niet te hameren met verzoeken.
De Typeahead JS
Dit roept in onze JS-functie (, gedefinieerd in 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
}
}
}
Zoals je kunt zien is dit vrij simpel. ( veel van de complexiteit gaat over de up en down-toets om resultaten te selecteren.
Dit post naar ons. SearchApi
Als een resultaat gekies is navigeren we naar de url van het resultaat.
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
Ik veranderde ook het afhalen om te werken met HTMX, dit verandert simpelweg de search methode om een HTMX refresh te gebruiken:
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
}
Let op dat we de innerHTML van de contentcontainer Met het resultaat van de zoekopdracht. Dit is een eenvoudige manier om de inhoud van de pagina met het resultaat te updaten zonder pagina-refrisering.
We veranderen ook de url in de geschiedenis naar de nieuwe url.
In slotting:
Dit voegt een krachtige maar eenvoudige zoekcapaciteit toe aan de site. Het geeft deze site een professioneler gevoel en maakt het makkelijker om te navigeren.