@@ -4,6 +4,7 @@ import lunr from 'lunr'
44import { qs , escapeHtmlEntities , isBlank , getQueryParamByName , getProjectNameAndVersion } from './helpers'
55import { setSearchInputValue } from './search-bar'
66import searchResultsTemplate from './handlebars/templates/search-results.handlebars'
7+ import { getSearchNodes } from './globals'
78
89const EXCERPT_RADIUS = 80
910const SEARCH_CONTAINER_SELECTOR = '#search'
@@ -26,30 +27,85 @@ function initialize () {
2627 const pathname = window . location . pathname
2728 if ( pathname . endsWith ( '/search.html' ) || pathname . endsWith ( '/search' ) ) {
2829 const query = getQueryParamByName ( 'q' )
29- search ( query )
30+ const queryType = getQueryParamByName ( 'type' )
31+ search ( query , queryType )
3032 }
3133}
3234
33- async function search ( value ) {
35+ async function search ( value , queryType ) {
3436 if ( isBlank ( value ) ) {
3537 renderResults ( { value } )
3638 } else {
3739 setSearchInputValue ( value )
3840
39- const index = await getIndex ( )
40-
4141 try {
42- // We cannot match on atoms :foo because that would be considered
43- // a filter. So we escape all colons not preceded by a word.
44- const fixedValue = value . replaceAll ( / ( \B | \\ ) : / g, '\\:' )
45- const results = searchResultsToDecoratedSearchItems ( index . search ( fixedValue ) )
42+ let results = [ ]
43+ const searchNodes = getSearchNodes ( )
44+
45+ if ( [ 'related' , 'latest' ] . includes ( queryType ) && searchNodes . length > 0 ) {
46+ results = await remoteSearch ( value , queryType , searchNodes )
47+ } else {
48+ results = await localSearch ( value )
49+ }
50+
4651 renderResults ( { value, results } )
4752 } catch ( error ) {
4853 renderResults ( { value, errorMessage : error . message } )
4954 }
5055 }
5156}
5257
58+ async function localSearch ( value ) {
59+ const index = await getIndex ( )
60+
61+ // We cannot match on atoms :foo because that would be considered
62+ // a filter. So we escape all colons not preceded by a word.
63+ const fixedValue = value . replaceAll ( / ( \B | \\ ) : / g, '\\:' )
64+ return searchResultsToDecoratedSearchItems ( index . search ( fixedValue ) )
65+ }
66+
67+ async function remoteSearch ( value , queryType , searchNodes ) {
68+ let filterNodes = searchNodes
69+
70+ if ( queryType === 'latest' ) {
71+ filterNodes = searchNodes . slice ( 0 , 1 )
72+ }
73+
74+ const filters = filterNodes . map ( node => `package:=${ node . name } -${ node . version } ` ) . join ( ' || ' )
75+
76+ const params = new URLSearchParams ( )
77+ params . set ( 'q' , value )
78+ params . set ( 'query_by' , 'title,doc' )
79+ params . set ( 'filter_by' , filters )
80+
81+ const response = await fetch ( `https://search.hexdocs.pm/?${ params . toString ( ) } ` )
82+ const payload = await response . json ( )
83+
84+ if ( Array . isArray ( payload . hits ) ) {
85+ return payload . hits . map ( result => {
86+ const [ packageName , packageVersion ] = result . document . package . split ( '-' )
87+
88+ const doc = result . document . doc
89+ const excerpts = [ doc ]
90+ const metadata = { }
91+ const ref = `https://hexdocs.pm/${ packageName } /${ packageVersion } /${ result . document . ref } `
92+ const title = result . document . title
93+ const type = result . document . type
94+
95+ return {
96+ doc,
97+ excerpts,
98+ metadata,
99+ ref,
100+ title,
101+ type
102+ }
103+ } )
104+ } else {
105+ return [ ]
106+ }
107+ }
108+
53109function renderResults ( { value, results, errorMessage } ) {
54110 const searchContainer = qs ( SEARCH_CONTAINER_SELECTOR )
55111 const resultsHtml = searchResultsTemplate ( { value, results, errorMessage } )
0 commit comments