Skip to content

Commit

Permalink
Docs/client search with js search (#11505)
Browse files Browse the repository at this point in the history
* revised draft with the changes applied. Example rebased

* Style edits

Grammar / typo fixes and various small edits to align with tone of existing docs

* linted example files

* Run linting
  • Loading branch information
jonniebigodes authored and m-allanson committed Feb 12, 2019
1 parent ab90f46 commit bab4eae
Show file tree
Hide file tree
Showing 9 changed files with 1,127 additions and 3 deletions.
576 changes: 573 additions & 3 deletions docs/docs/adding-search-with-js-search.md

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions examples/using-js-search/README.md
@@ -0,0 +1,6 @@
## Adding client search using Js Search

The code in this folder is the full implementation for the documentation on how to add client search with [js-search](https://github.com/bvaughn/js-search).

A live version of this example is located [here](https://pedantic-clarke-873963.netlify.com/)
The endpoint that uses Gatsby API is located [here](https://pedantic-clarke-873963.netlify.com/search)
41 changes: 41 additions & 0 deletions examples/using-js-search/gatsby-node.js
@@ -0,0 +1,41 @@
const path = require(`path`)
const axios = require(`axios`)

exports.createPages = ({ actions }) => {
const { createPage } = actions
return new Promise((resolve, reject) => {
axios
.get(`https://bvaughn.github.io/js-search/books.json`)
.then(result => {
const { data } = result
/**
* creates a page dynamic page with the data recieved
* injects the data recived into the context object alongside with some options
* to configure js-search
*/
createPage({
path: `/search`,
component: path.resolve(`./src/templates/ClientSearchTemplate.js`),
context: {
bookData: {
allBooks: data.books,
options: {
indexStrategy: `Prefix match`,
searchSanitizer: `Lower Case`,
TitleIndex: true,
AuthorIndex: true,
SearchByTerm: true,
},
},
},
})
resolve()
})
.catch(err => {
console.log(`====================================`)
console.log(`error creating Page:${err}`)
console.log(`====================================`)
reject(new Error(`error on page creation:\n${err}`))
})
})
}
24 changes: 24 additions & 0 deletions examples/using-js-search/package.json
@@ -0,0 +1,24 @@
{
"name": "js-search-example",
"private": true,
"description": "Gatsby example site to demonstrate client side search",
"version": "0.1.0",
"keywords": [
"Gatsby", "client side search", "js-search"
],
"license": "MIT",
"scripts": {
"build": "gatsby build",
"develop": "gatsby develop",
"start": "npm run develop",
"serve": "gatsby serve",
"test": "echo \"Write tests! -> https://gatsby.app/unit-testing\""
},
"dependencies": {
"axios": "^0.18.0",
"gatsby": "^2.0.104",
"js-search": "^1.4.2",
"react": "^16.5.1",
"react-dom": "^16.5.1"
}
}
224 changes: 224 additions & 0 deletions examples/using-js-search/src/components/ClientSearch.js
@@ -0,0 +1,224 @@
import React, { Component } from "react"
import * as JsSearch from "js-search"

class ClientSearch extends Component {
state = {
isLoading: true,
searchResults: [],
search: null,
isError: false,
indexByTitle: false,
indexByAuthor: false,
termFrequency: true,
removeStopWords: false,
searchQuery: ``,
selectedStrategy: ``,
selectedSanitizer: ``,
}

static getDerivedStateFromProps(nextProps, prevState) {
if (prevState.search === null) {
const { engine } = nextProps
return {
indexByTitle: engine.TitleIndex,
indexByAuthor: engine.AuthorIndex,
termFrequency: engine.SearchByTerm,
selectedSanitizer: engine.searchSanitizer,
selectedStrategy: engine.indexStrategy,
}
}
return null
}
async componentDidMount() {
this.rebuildIndex()
}

rebuildIndex = () => {
const {
selectedStrategy,
selectedSanitizer,
removeStopWords,
termFrequency,
indexByTitle,
indexByAuthor,
} = this.state
const { books } = this.props

const dataToSearch = new JsSearch.Search(`isbn`)
if (removeStopWords) {
dataToSearch.tokenizer = new JsSearch.StopWordsTokenizer(
dataToSearch.tokenizer
)
}
if (selectedStrategy === `All`) {
dataToSearch.indexStrategy = new JsSearch.AllSubstringsIndexStrategy()
}
if (selectedStrategy === `Exact match`) {
dataToSearch.indexStrategy = new JsSearch.ExactWordIndexStrategy()
}
if (selectedStrategy === `Prefix match`) {
dataToSearch.indexStrategy = new JsSearch.PrefixIndexStrategy()
}
/* eslint-disable */
selectedSanitizer === `Case Sensitive`
? (dataToSearch.sanitizer = new JsSearch.CaseSensitiveSanitizer())
: (dataToSearch.sanitizer = new JsSearch.LowerCaseSanitizer())

termFrequency === true
? (dataToSearch.searchIndex = new JsSearch.TfIdfSearchIndex(`isbn`))
: (dataToSearch.searchIndex = new JsSearch.UnorderedSearchIndex())
/* eslint-enable */
if (indexByTitle) {
dataToSearch.addIndex(`title`)
}
if (indexByAuthor) {
dataToSearch.addIndex(`author`)
}
dataToSearch.addDocuments(books)
this.setState({ search: dataToSearch, isLoading: false })
}
searchData = e => {
const { search } = this.state
const queryResult = search.search(e.target.value)
this.setState({ searchQuery: e.target.value, searchResults: queryResult })
}
handleSubmit = e => {
e.preventDefault()
}
render() {
const { isLoading, isError, searchResults, searchQuery } = this.state
const { books } = this.props
const queryResults = searchQuery === `` ? books : searchResults
if (isLoading) {
return (
<div>
<h1 style={{ marginTop: `3em` }}>Getting the search all setup</h1>
</div>
)
}
if (isError) {
return (
<div>
<h1 style={{ marginTop: `3em`, textAlign: `center` }}>Ohh no!!!!!</h1>
<h3
style={{
marginTop: `2em`,
padding: `2em 0em`,
textAlign: `center`,
}}
>
Something really bad happened
</h3>
</div>
)
}
return (
<div>
<div style={{ margin: `0 auto` }}>
<form onSubmit={this.handleSubmit}>
<div style={{ margin: `0 auto` }}>
<label htmlFor="Search" style={{ paddingRight: `10px` }}>
Enter your search here
</label>
<input
id="Search"
value={searchQuery}
onChange={this.searchData}
placeholder="Enter your search here"
style={{ margin: `0 auto`, width: `400px` }}
/>
</div>
</form>
<div>
Number of items:
{queryResults.length}
<table
style={{
width: `100%`,
borderCollapse: `collapse`,
borderRadius: `4px`,
border: `1px solid #d3d3d3`,
}}
>
<thead style={{ border: `1px solid #808080` }}>
<tr>
<th
style={{
textAlign: `left`,
padding: `5px`,
fontSize: `14px`,
fontWeight: 600,
borderBottom: `2px solid #d3d3d3`,
cursor: `pointer`,
}}
>
Book ISBN
</th>
<th
style={{
textAlign: `left`,
padding: `5px`,
fontSize: `14px`,
fontWeight: 600,
borderBottom: `2px solid #d3d3d3`,
cursor: `pointer`,
}}
>
Book Title
</th>
<th
style={{
textAlign: `left`,
padding: `5px`,
fontSize: `14px`,
fontWeight: 600,
borderBottom: `2px solid #d3d3d3`,
cursor: `pointer`,
}}
>
Book Author
</th>
</tr>
</thead>
<tbody>
{/* eslint-disable */}
{queryResults.map(item => {
return (
<tr key={`row_${item.isbn}`}>
<td
style={{
fontSize: `14px`,
border: `1px solid #d3d3d3`,
}}
>
{item.isbn}
</td>
<td
style={{
fontSize: `14px`,
border: `1px solid #d3d3d3`,
}}
>
{item.title}
</td>
<td
style={{
fontSize: `14px`,
border: `1px solid #d3d3d3`,
}}
>
{item.author}
</td>
</tr>
)
})}
{/* eslint-enable */}
</tbody>
</table>
</div>
</div>
</div>
)
}
}
export default ClientSearch

0 comments on commit bab4eae

Please sign in to comment.