Skip to content

Commit

Permalink
perf: remove @orama/plugin-match-highlight in favor of @orama/highlig…
Browse files Browse the repository at this point in the history
…ht (#545)

Co-authored-by: Michele Riva <ciao@micheleriva.it>
  • Loading branch information
raiindev and micheleriva authored Nov 10, 2023
1 parent 1d3ef7a commit a56f547
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 152 deletions.
94 changes: 47 additions & 47 deletions packages/docs/public/sitemap-0.xml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/plugin-docusaurus/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
"@algolia/autocomplete-js": "^1.7.2",
"@algolia/autocomplete-theme-classic": "^1.7.3",
"@docusaurus/theme-common": "^2.4.3",
"@orama/highlight": "^0.1.2",
"@orama/orama": "workspace:*",
"@orama/plugin-match-highlight": "workspace:*",
"@orama/plugin-parsedoc": "workspace:*",
"github-slugger": "^2.0.0",
"pako": "^2.1.0",
Expand Down
77 changes: 42 additions & 35 deletions packages/plugin-docusaurus/src/client/theme/SearchBar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,38 @@
import { autocomplete } from '@algolia/autocomplete-js'
import '@algolia/autocomplete-theme-classic/dist/theme.min.css'
import { autocomplete } from "@algolia/autocomplete-js"
import "@algolia/autocomplete-theme-classic/dist/theme.min.css"
// eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error
// @ts-ignore Will fail in CJS compilation
import { GlobalVersion, useActiveVersion, useVersions } from '@docusaurus/plugin-content-docs/client'
import { useColorMode, useDocsPreferredVersion } from '@docusaurus/theme-common'
import useBaseUrl from '@docusaurus/useBaseUrl'
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
import { usePluginData } from '@docusaurus/useGlobalData'
import useIsBrowser from '@docusaurus/useIsBrowser'
import { create, load, AnyDocument } from '@orama/orama'
import type { OramaWithHighlight, Position } from '@orama/plugin-match-highlight'
import { searchWithHighlight } from '@orama/plugin-match-highlight'
import { ungzip } from 'pako'
import { Fragment, createElement, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { render } from 'react-dom'
import { GlobalVersion, useActiveVersion, useVersions } from "@docusaurus/plugin-content-docs/client"
import { useColorMode, useDocsPreferredVersion } from "@docusaurus/theme-common"
import useBaseUrl from "@docusaurus/useBaseUrl"
import useDocusaurusContext from "@docusaurus/useDocusaurusContext"
import { usePluginData } from "@docusaurus/useGlobalData"
import useIsBrowser from "@docusaurus/useIsBrowser"
import { AnyDocument, create, load, Orama, RawData, search as oramaSearch } from "@orama/orama"
import { Highlight } from "@orama/highlight"
import { ungzip } from "pako"
import { createElement, Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react"
import { render } from "react-dom"
// @ts-expect-error Resolve at runtime
import { SearchNoResults } from '@theme/SearchNoResults'
import { SearchNoResults } from "@theme/SearchNoResults"
// @ts-expect-error Resolve at runtime
import { SearchResults } from '@theme/SearchResults'
import { SearchResults } from "@theme/SearchResults"
// @ts-expect-error Resolve at runtime
import { SearchResult } from '@theme/SearchResult'
import { Hit, INDEX_FILE, PLUGIN_NAME, PluginData, RawDataWithPositions, schema } from '../../../server/types.js'
import { SearchResult } from "@theme/SearchResult"
import { Hit, INDEX_FILE, PLUGIN_NAME, PluginData, schema } from "../../../server/types.js"

const highlighter = new Highlight({
CSSClass: 'aa-ItemContentHighlight',
HTMLTag: 'span',
})

export default function SearchBar(): JSX.Element {
const isBrowser = useIsBrowser()
const { siteConfig } = useDocusaurusContext()
const containerRef = useRef<HTMLDivElement>(null)
const { colorMode } = useColorMode()
const { searchData } = usePluginData(PLUGIN_NAME) as PluginData
const [database, setDatabase] = useState<OramaWithHighlight<AnyDocument>>()
const [database, setDatabase] = useState<Orama<AnyDocument>>()
const searchBaseUrl = useBaseUrl(INDEX_FILE)
const versions = useVersions(undefined)
const activeVersion = useActiveVersion(undefined)
Expand Down Expand Up @@ -79,21 +83,24 @@ export default function SearchBar(): JSX.Element {
{
sourceId: 'orama',
async getItems() {
const results = await searchWithHighlight(database, {
if(!term) {
return []
}

const results = await oramaSearch(database, {
term,
properties: ['sectionTitle', 'sectionContent', 'type']
})

const processed = results.hits.flatMap(hit =>
Object.values((hit as any).positions.sectionContent).flatMap(positions =>
(positions as any).map((position: Position) => ({
...hit,
position
}))
)
)

return processed
return results.hits.flatMap((hit) => {
return {
...hit,
document: {
...hit.document,
sectionContent: highlighter.highlight(hit.document.sectionContent, term).trim(20),
}
}
})
},
getItemUrl({ item }: { item: Hit }) {
return item.document.pageRoute
Expand Down Expand Up @@ -151,13 +158,13 @@ export default function SearchBar(): JSX.Element {
}

const deflated = ungzip(buffer, { to: 'string' })
const data: RawDataWithPositions = JSON.parse(deflated)
const data: RawData = JSON.parse(deflated)

const _db = await create({ schema })
const db = _db as OramaWithHighlight<typeof _db>;
await load(db, data)
db.data.positions = data.positions
setDatabase(db)

await load(_db, data)

setDatabase(_db)
}

if (!isBrowser || !version) {
Expand Down
12 changes: 12 additions & 0 deletions packages/plugin-docusaurus/src/client/theme/SearchBar/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,15 @@
font-size: 0.9em;
color: var(--ifm-color-emphasis-600);
}

.aa-ItemLink {
margin-bottom: .6rem;
text-decoration: none !important;
color: rgba(var(--aa-primary-color-rgb), 1) !important;
padding: calc(var(--aa-spacing-half)/1.2);
}

.aa-ItemContentHighlight {
color: rgba(var(--aa-primary-color-rgb), 1);
font-weight: bold;
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function SearchBarFooter({ translations = {} }: SearchBarFooterProps): JS
navigateDownKeyAriaLabel = 'Arrow down',
closeText = 'to close',
closeKeyAriaLabel = 'Escape key',
searchByText = 'Search by'
searchByText = 'Powered by'
} = translations

const { colorMode } = useColorMode()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,5 @@ a.aa-FooterSearchCredit span {
}

a.aa-FooterSearchCredit svg {
min-width: 8em;
min-width: 7em;
}
40 changes: 5 additions & 35 deletions packages/plugin-docusaurus/src/client/theme/SearchResult/index.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,8 @@
import { Result } from '@orama/orama'
import { Position } from '@orama/plugin-match-highlight'
import { SectionSchema } from '../../../server/types.js'

type Hit = Result<SectionSchema> & { position: Position }
import { Result } from "@orama/orama"
import { SectionSchema } from "../../../server/types.js"

interface SearchResultProps {
hit: Hit
}

function snippet(hit: Hit): JSX.Element {
const PADDING = 20
const PADDING_MARKER = '...'
const isBeginning = hit.position.start < PADDING
const isEnd = hit.position.start + hit.position.length > (hit.document.sectionContent as string).length - PADDING
const preMatch = (hit.document.sectionContent as string).substring(
isBeginning ? 0 : hit.position.start - PADDING,
hit.position.start
)
const match = (hit.document.sectionContent as string).substring(
hit.position.start,
hit.position.start + hit.position.length
)
const postMatch = (hit.document.sectionContent as string).substring(
hit.position.start + hit.position.length,
hit.position.start + hit.position.length + PADDING
)
return (
<p>
{isBeginning ? '' : PADDING_MARKER}
{preMatch}
<u>{match}</u>
{postMatch}
{isEnd ? '' : PADDING_MARKER}
</p>
)
hit: Result<SectionSchema>
}

export function SearchResult({ hit }: SearchResultProps): JSX.Element {
Expand All @@ -44,7 +13,8 @@ export function SearchResult({ hit }: SearchResultProps): JSX.Element {
<div className="aa-ItemContentTitle">
<h5 style={{ marginBottom: 0 }}>{hit.document.sectionTitle as string}</h5>
</div>
<div className="aa-ItemContentDescription">{snippet(hit)}</div>
<div className="aa-ItemContentDescription"
dangerouslySetInnerHTML={{ __html: hit.document.sectionContent }}></div>
</div>
</div>
</a>
Expand Down
19 changes: 5 additions & 14 deletions packages/plugin-docusaurus/src/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { LoadedContent, LoadedVersion } from '@docusaurus/plugin-content-docs'
import type { LoadContext, Plugin } from '@docusaurus/types'
import { create, insertMultiple, save } from '@orama/orama'
import { OramaWithHighlight, afterInsert as highlightAfterInsert } from '@orama/plugin-match-highlight'
import type { DefaultSchemaElement, NodeContent, PopulateFnContext } from '@orama/plugin-parsedoc'
import { defaultHtmlSchema, populate } from '@orama/plugin-parsedoc'
import * as githubSlugger from 'github-slugger'
Expand All @@ -13,9 +12,9 @@ import { gzip as gzipCB } from 'node:zlib'
import type { Configuration as WebpackConfiguration } from 'webpack'

import { retrieveTranslationMessages } from './translationMessages.js'
import { INDEX_FILE, PLUGIN_NAME, PluginOptions, RawDataWithPositions, SectionSchema, schema } from './types.js'
import { INDEX_FILE, PLUGIN_NAME, PluginOptions, SectionSchema, schema } from './types.js'

export type { PluginData, PluginOptions, RawDataWithPositions, SectionSchema } from './types.js'
export type { PluginData, PluginOptions, SectionSchema } from './types.js'

const gzip = promisify(gzipCB)

Expand Down Expand Up @@ -146,20 +145,12 @@ async function buildDevSearchData(siteDir: string, outDir: string, allContent: a

// Create the Orama database and then serialize it
const _db = await create({
schema,
plugins: [
{
name: 'highlight',
afterInsert: highlightAfterInsert
}
]
schema
})
const db = _db as OramaWithHighlight<typeof _db>

await insertMultiple(db, documents)
await insertMultiple(_db, documents)

const serialized = (await save(db)) as RawDataWithPositions
serialized.positions = db.data.positions
const serialized = (await save(_db))

await writeFile(indexPath(outDir, version), await gzip(JSON.stringify(serialized)))
}
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-docusaurus/src/server/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Result } from '@orama/orama'
import type { Position } from '@orama/plugin-match-highlight'
import type { Position } from '@orama/highlight'

import type { AnyDocument, AnySchema, RawData } from '@orama/orama'

Expand Down
21 changes: 7 additions & 14 deletions packages/plugin-docusaurus/test/integration.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { AnyOrama, TypedDocument, create, load } from '@orama/orama'
import { OramaWithHighlight, SearchResultWithHighlight, searchWithHighlight } from '@orama/plugin-match-highlight'
import { create, load, search } from "@orama/orama"
import assert from 'node:assert'
import { exec, ExecException } from 'node:child_process'
import { existsSync } from 'node:fs'
Expand Down Expand Up @@ -28,10 +27,6 @@ async function cleanup(): Promise<void> {
await rm(sandbox, { force: true, recursive: true })
}

function search<T extends AnyOrama, ResultDocument = TypedDocument<T>>(database: OramaWithHighlight<T>, term: string): Promise<SearchResultWithHighlight<ResultDocument>> {
return searchWithHighlight(database, { term, properties: ['sectionTitle', 'sectionContent', 'type'] })
}

async function execute(command: string, cwd?: string): Promise<Execution> {
const { HOME, PATH } = process.env
const env = cwd ? { HOME, PATH } : process.env
Expand Down Expand Up @@ -94,31 +89,29 @@ await test('generated DBs have indexed pages content', async () => {
const rawData = gunzipSync(rawCompressedData).toString('utf-8')
const data = JSON.parse(rawData)

const _database = await create({ schema })
const database = _database as OramaWithHighlight<typeof _database>;
const database = await create({ schema })
await load(database, data)
database.data.positions = data.positions

// Search results seem reasonable
const indexSearchResult = await search(database, 'index')
const indexSearchResult = await search(database, { term: 'index', properties: ['sectionTitle', 'sectionContent', 'type'] })
assert.ok(indexSearchResult.count === 1)
assert.ok(indexSearchResult.hits[0].document.pageRoute === '/#main')

const catSearchResult = await search(database, 'cat')
const catSearchResult = await search(database, { term: 'cat', properties: ['sectionTitle', 'sectionContent', 'type'] })
assert.ok(catSearchResult.count === 1)
assert.ok(catSearchResult.hits[0].document.pageRoute === '/animals_cat')

const dogSearchResult = await search(database, 'dog')
const dogSearchResult = await search(database, { term: 'dog', properties: ['sectionTitle', 'sectionContent', 'type'] })
assert.ok(dogSearchResult.count === 2)
assert.ok(dogSearchResult.hits[0].document.pageRoute === '/animals_dog#dog')

const domesticSearchResult = await search(database, 'domestic')
const domesticSearchResult = await search(database, { term: 'domestic', properties: ['sectionTitle', 'sectionContent', 'type'] })
assert.ok(domesticSearchResult.count === 2)
assert.ok(domesticSearchResult.hits[0].document.pageRoute === '/animals_cat')
assert.ok(domesticSearchResult.hits[1].document.pageRoute === '/animals_dog#dog')

// We do not have content about turtles
const turtleSearchResult = await search(database, 'turtle')
const turtleSearchResult = await search(database, { term: 'turtle', properties: ['sectionTitle', 'sectionContent', 'type'] })
assert.ok(turtleSearchResult.count === 0)
})

Expand Down
17 changes: 14 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 comment on commit a56f547

@vercel
Copy link

@vercel vercel bot commented on a56f547 Nov 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.