Skip to content

Instantly share code, notes, and snippets.

@tao
Created June 15, 2021 14:48
Show Gist options
  • Save tao/a40862ecc19043e977a6dbb93f13badb to your computer and use it in GitHub Desktop.
Save tao/a40862ecc19043e977a6dbb93f13badb to your computer and use it in GitHub Desktop.
Statamic Typesense: multi-search on the client example
<html>
<head>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
<script src="//unpkg.com/alpinejs" defer></script>
<style>
[x-cloak] {
display: none !important;
}
</style>
</head>
<body>
<div class="relative"
x-data="{
searchString: '',
currentIndex: window.searchIndex,
searchResults: [],
indexes: {
results: [],
},
async search() {
this.state = 'searching';
// MULTI SEARCH
let multiRequest = _getCollectionsToSearch(this.currentIndex);
let commonSearchParams = _getCommonSearchParams(this.searchString);
this.searchResults = await window.typesense.multiSearch.perform({
searches: multiRequest
}, commonSearchParams)
.then(function (searchResults) {
return searchResults.results
}).catch(function (error) {
console.log(error)
})
},
async listIndexes() {
// this.indexes.results = await window.typesense.collections().retrieve();
return this.indexes.results = _getSearchIndexes();
},
resetSearch() {
document.getElementById('js-search-bar').focus()
this.search();
},
}"
>
<div class="flex flex-col justify-center p-8">
<!-- search input -->
<form id="js-search-form" action="javascript:void(0);" class="flex w-full items-center space-x-4">
<input x-model="searchString" x-on:input="search()" type="search" id="js-search-bar" class="shadow appearance-none border border-medium rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline" name="q" placeholder="Start typing to search..." autocomplete="off" autofocus>
</form>
<!-- search results -->
<div
x-cloak
x-show="searchString.length > 0"
>
<template x-for="results in searchResults" :key="results.request_params.collection_name">
<div class="py-16">
<div class="flex justify-between text-gray-500 pb-4">
<h3 x-text="results.request_params.collection_name" class="font-bold capitalize"></h3>
<p class="text-meta" x-text="results.found === 0 ? 'No results.' : (results.found + ' results')"></p>
</div>
<div class="content">
<ul class="space-y-4 divide-y divide-dashed search--results">
<template x-for='result in _formatResults(results.hits)' :key="result.document.id">
<li class="p-4 hover:bg-gray-100 rounded">
<a
:href="result.document.url"
class="mt-8"
>
<p class="text-base font-medium text-blue-600 mb-0" x-html="result.highlights.title ?? result.document.title"></p>
<p class="text-meta text-sm mb-0" x-html="result.highlights.content ?? result.document.synopsis ?? ''"></p>
</a>
</li>
</template>
</ul>
</div>
</div>
</template>
</div>
</div>
</div>
<!-- Alpine: add x-html -->
<script>
document.addEventListener('alpine:initializing', () => {
Alpine.directive('html', (el, { expression }, { evaluateLater, effect }) => {
let getHtml = evaluateLater(expression)
effect(() => {
getHtml(html => {
el.innerHTML = html
})
})
})
})
</script>
<!-- Typesense -->
<script src="https://cdn.jsdelivr.net/npm/typesense@latest/dist/typesense.min.js"></script>
<script>
window.typesense = new Typesense.Client({
'nodes': [
{
'host': TYPESENSE_SERVER_URL,
'port': '443',
'protocol': 'https'
}
],
'apiKey': TYPESENSE_API_KEY,
'numRetries': 3, // A total of 4 tries (1 original try + 3 retries)
'connectionTimeoutSeconds': 10,
'logLevel': 'debug'
});
window.searchIndex = 'places';
window.locale = 'en';
</script>
<!-- Search helpers -->
<script>
function _formatResults(searchResults) {
if (!searchResults) return [];
// format highlights in each search result
return searchResults.map(result => {
// convert highlights to object
let highlights = {};
result.highlights.map(el => {
highlights[el.field] = el.snippet
});
return { ...result, highlights };
})
}
function _getCommonSearchParams(searchQuery) {
return {
query_by: 'title, content',
q: searchQuery,
highlight_affix_num_tokens: 20,
highlight_start_tag: '<mark>',
highlight_end_tag: '</mark>',
filter_by: ('locale:'+window.locale),
};
}
function _getCollectionsToSearch(index, options) {
switch (index) {
case 'places': {
let cinemas = {
collection: 'cinemas',
};
return [cinemas]
}
case 'library': {
return [
{
collection: 'movies',
},
{
collection: 'series',
},
];
}
case 'other': {
return [
{
collection: 'news',
},
];
}
case 'everything':
default: {
return [
//
];
}
}
}
function _getSearchIndexes() {
return [
{ name: 'places' },
{ name: 'library' },
{ name: 'other' },
];
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment