Skip to content

Commit

Permalink
feat($plugin-search): improve the native search algorithm (vuejs#1557)
Browse files Browse the repository at this point in the history
  • Loading branch information
ynnelson authored and Sergey Larionov committed Aug 19, 2020
1 parent 870118d commit 04fae73
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 10 deletions.
13 changes: 5 additions & 8 deletions packages/@vuepress/plugin-search/SearchBox.vue
Expand Up @@ -45,6 +45,8 @@
</template>

<script>
import matchQuery from './match-query'
/* global SEARCH_MAX_SUGGESTIONS, SEARCH_PATHS, SEARCH_HOTKEYS */
export default {
name: 'SearchBox',
Expand Down Expand Up @@ -76,11 +78,6 @@ export default {
const { pages } = this.$site
const max = this.$site.themeConfig.searchMaxSuggestions || SEARCH_MAX_SUGGESTIONS
const localePath = this.$localePath
const matches = item => (
item
&& item.title
&& item.title.toLowerCase().indexOf(query) > -1
)
const res = []
for (let i = 0; i < pages.length; i++) {
if (res.length >= max) break
Expand All @@ -95,13 +92,13 @@ export default {
continue
}
if (matches(p)) {
if (matchQuery(query, p)) {
res.push(p)
} else if (p.headers) {
for (let j = 0; j < p.headers.length; j++) {
if (res.length >= max) break
const h = p.headers[j]
if (matches(h)) {
if (h.title && matchQuery(query, p, h.title)) {
res.push(Object.assign({}, p, {
path: p.path + '#' + h.slug,
header: h
Expand Down Expand Up @@ -227,7 +224,7 @@ export default {
background #fff
width 20rem
position absolute
top 1.5rem
top 2 rem
border 1px solid darken($borderColor, 10%)
border-radius 6px
padding 0.4rem
Expand Down
42 changes: 42 additions & 0 deletions packages/@vuepress/plugin-search/__tests__/matchQuery.spec.js
@@ -0,0 +1,42 @@
import matchQuery from '../match-query'

describe('matchQuery', () => {
const page = {
title: 'HoMe PaGe',
frontmatter: {
tags: ['vuepress', 'is', 'jUst AwEsOme']
}
}

test('should match when query includes part of the page title', () => {
const query = 'hom'

const match = matchQuery(query, page)

expect(match).toBe(true)
})

test('should match when query includes the full page title', () => {
const query = 'home page'

const match = matchQuery(query, page)

expect(match).toBe(true)
})

test('should match when query includes a tag', () => {
const query = 'vuepress'

const match = matchQuery(query, page)

expect(match).toBe(true)
})

test('should match when query includes part of tag', () => {
const query = 'just aw'

const match = matchQuery(query, page)

expect(match).toBe(true)
})
})
42 changes: 42 additions & 0 deletions packages/@vuepress/plugin-search/match-query.js
@@ -0,0 +1,42 @@

import get from 'lodash/get'

export default (query, page, additionalStr = null) => {
let domain = get(page, 'title', '')

if (get(page, 'frontmatter.tags')) {
domain += ` ${page.frontmatter.tags.join(' ')}`
}

if (additionalStr) {
domain += ` ${additionalStr}`
}

return matchTest(query, domain)
}

const matchTest = (query, domain) => {
const escapeRegExp = str => str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')

const words = query
.split(/\s+/g)
.map(str => str.trim())
.filter(str => !!str)
const hasTrailingSpace = query.endsWith(' ')
const searchRegex = new RegExp(
words
.map((word, index) => {
if (words.length === index + 1 && !hasTrailingSpace) {
// The last word - ok with the word being "startswith"-like
return `(?=.*\\b${escapeRegExp(word)})`
} else {
// Not the last word - expect the whole word exactly
return `(?=.*\\b${escapeRegExp(word)}\\b)`
}
})
.join('') + '.+',
'gi'
)
return searchRegex.test(domain)
}

7 changes: 7 additions & 0 deletions packages/docs/docs/guide/frontmatter.md
Expand Up @@ -49,6 +49,13 @@ lang = "en-US"

Title of current page.

### tags

- Type: `array`
- Default: `undefined`

You can use tags to improve [built-in search](/theme/default-theme-config.html#built-in-search).

### lang

- Type: `string`
Expand Down
13 changes: 11 additions & 2 deletions packages/docs/docs/theme/default-theme-config.md
Expand Up @@ -357,10 +357,19 @@ search: false
```

::: tip
Built-in Search only builds index from the title, `h2` and `h3` headers, if you need full text search, you can use [Algolia DocSearch](#algolia-docsearch).
Built-in Search only builds index from the title, `h2` and `h3` headers and any tags added with `YAML front matter` as shown below, if you need full text search, you can use [Algolia Search](#algolia-search).
:::

### Algolia DocSearch
```yaml
---
tags:
- configuration
- theme
- indexing
---
```

### Algolia Search

The `themeConfig.algolia` option allows you to use [Algolia DocSearch](https://community.algolia.com/docsearch/) to replace the simple built-in search. To enable it, you need to provide at least `apiKey` and `indexName`:

Expand Down

0 comments on commit 04fae73

Please sign in to comment.