Skip to content

Commit

Permalink
feat: add search (#2146)
Browse files Browse the repository at this point in the history
Co-authored-by: Farnabaz <farnabaz@gmail.com>
  • Loading branch information
Barbapapazes and farnabaz committed Oct 27, 2023
1 parent 3cea513 commit f40e1a1
Show file tree
Hide file tree
Showing 19 changed files with 1,064 additions and 916 deletions.
53 changes: 53 additions & 0 deletions docs/content/1.get-started/2.configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,56 @@ export default defineNuxtConfig({
- Default: `{}`

Options for yaml parser.

## `experimental`

### `search`

- Type: `boolean | undefined`{lang=ts}
- Default: `undefined`{lang=ts}

Enable search feature.

#### `indexed`

- Type: `boolean`{lang=ts}
- Default: `true`{lang=ts}

Enable indexed search. This will generate a search index file that allow faster and more performant search.

#### `ignoredTags`

- Type: `string[]`{lang=ts}
- Default: `['style', 'code']`{lang=ts}

List of tags to ignore when parsing content for the search API response. This is useful to avoid including code snippets in the search results or style that does not provide any useful information.

#### `filterQuery`

- Type: `QueryBuilderWhere`{lang=ts}
- Default: `{}`{lang=ts}

Query to ignore when parsing content for the search API response. This is useful to avoid including content that is not meant to be searchable like drafts or private content.

#### `options`

- Type: `object`{lang=ts}
- Default:

```ts
{
fields: ['title', 'content', 'titles'],
storeFields: ['title', 'content', 'titles'],
searchOptions: {
prefix: true,
fuzzy: 0.2,
boost: {
title: 4,
content: 2,
titles: 1
}
}
}
```

When the indexed search is enabled, this option will automatically configure both the API endpoint and the `searchContent` composable. For simple search, you will need to pass the options to the `searchContent` composable.
86 changes: 86 additions & 0 deletions docs/content/2.usage/3.search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Search

With some simple configuration and a composable, you can add search to your Nuxt Content site.

## Usage

First, you need to enable search in the `nuxt.config.js` file:

```js [nuxt.config.js]
export default defineNuxtConfig({
content: {
experimental: {
search: true
}
}
})
```

::alert{type="info"}
You can use the search feature with any [Nuxt Content source](/api/configuration#sources). You can also enable or not the [document-driven feature](/guide/writing/document-driven).
::

## Search

You can use the `searchContent` composable to search in your `content` directory:

```vue
<script lang="ts" setup>
const search = ref('')
const results = searchContent(search)
</script>
<template>
<main>
<input v-model="search">
<pre>{{ result }} </pre>
</main>
</template>
```

By default, the `searchContent` composable will used an indexed search. This mean that the search will be faster and the API response smaller and optimized **only usable by [`miniSearch`](https://lucaong.github.io/minisearch/)**.

::alert{type="info"}
Internally, the search feature, both `searchContent` and `/api/indexed-search.ts` endpoint, use [`miniSearch`](https://lucaong.github.io/minisearch/).
::

## Simple Search

If needed, you can disable the indexed search and use a simple search instead:

```js [nuxt.config.js]
export default defineNuxtConfig({
content: {
experimental: {
search: {
indexed: false
}
}
}
})
```

Remember that the simple search is **slower** and the API response **bigger**. You can still use the `searchContent` composable which is agnostic of the search type.

## Custom Search

Using the simple search, aka non-indexed search, you can use the tool of your choice to search in the API response. For example, you can use [`fuse.js`](https://fusejs.io/) with the integration of [`@vueuse/integrations`](https://vueuse.org/integrations/useFuse/#usefuse):

```ts
export default async function customSearchContent(search: Ref<string>) {
const runtimeConfig = useRuntimeConfig()
const { integrity, api } = runtimeConfig.public.content

const { data } = await useFetch(`${api.baseURL}/search${integrity ? '.' + integrity : ''}.json`)

const { results } = useFuse(search, data)

return results
}
```

## Configuration

Please, read the [configuration section](../../4.api/3.configuration.md#search) to learn more about the search configuration.
37 changes: 37 additions & 0 deletions docs/content/3.composables/7.search-content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# searchContent()

Used to search in your `content` directory.

```ts
const input = ref('')
const results = searchContent(input)
```

## Parameters

| Key | Type | Default | Description |
|----------------------|:------------------------------------------------:|---------|----------------------------------------------------------------------------------------------|
| `search` | `MaybeRefOrGetter<string>` | | The search input |
| `options` | `SearchContentOptions` | `{}` | The options |
| `options.miniSearch` | `MaybeRefOrGetter<MiniSearchOptions<T>>` | | The options passed to [`miniSearch`](https://lucaong.github.io/minisearch/) |
| `options.fetch` | `MaybeRefOrGetter<UseFetchOptions<string \| T>>` | | The options passed to [`useFetch`](https://nuxt.com/docs/api/composables/use-fetch#usefetch) |

## MiniSearchOptions

You can easily define the miniSearch options by using the `defineMiniSearchOptions` composable:

```ts
const miniSearchOptions = defineMiniSearchOptions({
fields: ['title', 'description', 'body']
})
```

Using these options allows you to modify how the search is performed. You can change the [fields that are searched, the weight of each field, and more](https://lucaong.github.io/minisearch/#search-options).

::alert{type="info"}
Only available when using the simple search, aka non-indexed search.
::

## UseFetchOptions

An option is provided to customize the behavior of the `useFetch` composable used internally to only fetch the search content on client and lazy. This could avoid fetching the content on SSR and adding content to the `_payload.josn` file improving the performance of your app since `_payload` is loaded for hydration.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
"test:unit": "nuxi prepare test/fixtures/basic && nuxi prepare test/fixtures/document-driven && vitest run"
},
"dependencies": {
"@vueuse/core": "^10.2.1",
"@vueuse/nuxt": "^10.2.1",
"@nuxt/kit": "^3.8.0",
"@nuxtjs/mdc": "^0.2.6",
"@vueuse/head": "^2.0.0",
Expand All @@ -54,6 +56,7 @@
"listhen": "^1.5.5",
"mdast-util-to-string": "^4.0.0",
"mdurl": "^1.0.1",
"minisearch": "^6.1.0",
"micromark": "^4.0.0",
"micromark-util-sanitize-uri": "^2.0.0",
"micromark-util-types": "^2.0.0",
Expand Down
28 changes: 28 additions & 0 deletions playground/search/components/search.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script lang="ts" setup>
const search = ref('')
// Usage for non indexed search
// const miniSearch = defineMiniSearchOptions({
// fields: ['title', 'content', 'titles'],
// storeFields: ['title', 'content', 'titles'],
// searchOptions: {
// prefix: true,
// fuzzy: 0.2,
// boost: {
// title: 4,
// content: 2,
// titles: 1
// }
// }
// })
// const result = await searchContent(search, { miniSearch })
// Usage for indexed search
const result = await searchContent(search)
</script>

<template>
<input v-model="search">

<pre>{{ result }}</pre>
</template>
3 changes: 3 additions & 0 deletions playground/search/content/_partial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Partial file

This is a partial file.
5 changes: 5 additions & 0 deletions playground/search/content/draft.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
_draft: true
---

# Draft file
Empty file.
45 changes: 45 additions & 0 deletions playground/search/content/javascript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
description: JavaScript, a programming language!
---

# JavaScript

JavaScript (/ˈdʒɑːvəskrɪpt/), often abbreviated as JS, is a programming language that is one of the core technologies of the World Wide Web, alongside HTML and CSS. As of 2023, 98.7% of websites use JavaScript on the client side for webpage behavior, often incorporating third-party libraries. All major web browsers have a dedicated JavaScript engine to execute the code on users' devices.

Another article.

For more information, see [Wikipedia](https://en.wikipedia.org/wiki/JavaScript).

## Hello World

```javascript
console.log("Hello World!");
```

::alert{type="info"}
This is a JavaScript alert!
::

## History

Hello from history, this is a test!

### Creation

Creation is another test!

### Adoption

## Other usage

### Server-side

This is a section about server-side.

#### Node.js

##### Hello World

### Desktop applications

## See also
9 changes: 9 additions & 0 deletions playground/search/content/php.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
description: PHP, a programming language!
---

# PHP

PHP is a general-purpose scripting language geared towards web development. It was originally created by Danish-Canadian programmer Rasmus Lerdorf in 1993 and released in 1995. The PHP reference implementation is now produced by the PHP Group. PHP was originally an abbreviation of Personal Home Page, but it now stands for the recursive initialism PHP: Hypertext Preprocessor.

For more information, see [Wikipedia](https://en.wikipedia.org/wiki/PHP).
5 changes: 5 additions & 0 deletions playground/search/content/programming.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"languages": [
"javascript"
]
}
19 changes: 19 additions & 0 deletions playground/search/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export default defineNuxtConfig({
ssr: false,

extends: ['../shared'],

content: {
experimental: {
search: {
mode: 'full-text',
indexed: true,
filterQuery: { _draft: false, _partial: false }
}
}
},

typescript: {
includeWorkspace: true
}
})
17 changes: 17 additions & 0 deletions playground/search/pages/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script lang="ts" setup>
const open = ref(false)
</script>

<template>
<div>
<h1>Search</h1>

<button @click="open = !open">
{{ open ? 'Close' : 'Open' }} Search
</button>

<Search v-if="open" />

<Search />
</div>
</template>

0 comments on commit f40e1a1

Please sign in to comment.