Skip to content

Instantly share code, notes, and snippets.

@simonorzel26
Created July 15, 2024 07:41
Show Gist options
  • Save simonorzel26/b02c1ebd06ad2429daac99f6cf83765e to your computer and use it in GitHub Desktop.
Save simonorzel26/b02c1ebd06ad2429daac99f6cf83765e to your computer and use it in GitHub Desktop.
Shadcn Next.js App router Search field using meilisearch api search adfree-recipes.com
// Only a script
// If you need to add documents as well
import { PrismaClient, Recipe } from '@prisma/client';
import { MeiliSearch } from 'meilisearch'
;(async () => {
const client = new MeiliSearch({
host: 'MEILISEARCH_API_URL',
apiKey: 'MEILISEARCH_ADMIN_API_KEY',
})
const prisma = new PrismaClient();
// An index is where the documents are stored.
const index = client.index('recipes')
const recipe = await prisma.recipe.findMany({
select: {
id: true,
name: true,
description: true,
keywords: true,
createdAt: true,
categories: {
include: {
category: true,
},
},
cuisines: {
include: {
cuisine: true,
},
},
},
});
const documents = recipe.map((recipe) => {
return {
id: recipe.id,
name: recipe.name,
description: recipe.description,
keywords: recipe.keywords,
createdAt: recipe.createdAt,
categories: recipe.categories.map((category) => category.category.name),
cuisines: recipe.cuisines.map((cuisine) => cuisine.cuisine.name),
}
});
// If the index 'movies' does not exist, Meilisearch creates it when you first add the documents.
let response = await index.addDocuments(documents)
console.log(response) // => { "uid": 0 }
})()
// Similar to adfree-recipes.com
// components/Search/index.tsx
'use client'
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "../ui/command"
import { useEffect, useState } from "react"
import { search } from "./search.actions"
export function Search() {
const [inputValue, setInputValue] = useState<string>("")
const [searchResults, setSearchResults] = useState<string[]>([])
useEffect(() => {
const handler = setTimeout(() => {
if (inputValue) {
search(inputValue).then((results: any) => {
setSearchResults(results.hits.map((result: any) => result.name))
})
} else {
setSearchResults([])
}
}, 100)
return () => {
clearTimeout(handler)
}
}, [inputValue])
return (
<Command className="rounded-lg border shadow-md" shouldFilter={false}>
<CommandInput placeholder="Type 'lemon cake or grilled veg'..." value={inputValue} onValueChange={setInputValue} />
<CommandList>
<CommandEmpty>{inputValue === "" ? "Start typing to load results" : "No results found."}</CommandEmpty>
<CommandGroup>
{searchResults.map((result: string) => (
<CommandItem key={result} value={result}>
{result}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
)
}
// Similar to adfree-recipes.com
// components/Search/search.actions.ts
'use server'
import { MeiliSearch } from 'meilisearch'
const client = new MeiliSearch({
host: 'MEILISEARCH_API_URL',
apiKey: 'APIKEY',
})
const index = client.index('index')
export const search = async (query: string) => {
const search = await index.search(query, {hitsPerPage: 5})
return search;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment