import { highlightMarks, cutWords, DocumentationEntry } from '../utils';
export interface SearchResult {
- page: DocumentationEntry;
highlights: { [field: string]: [number, number][] };
+ longestTerm: string;
+ page: DocumentationEntry;
}
interface Props {
*/
import * as React from 'react';
import lunr, { LunrIndex } from 'lunr';
+import { sortBy } from 'lodash';
import SearchResultEntry, { SearchResult } from './SearchResultEntry';
import { DocumentationEntry } from '../utils';
.map(match => {
const page = this.props.pages.find(page => page.relativeName === match.ref);
const highlights: { [field: string]: [number, number][] } = {};
+ let longestTerm = '';
+ // remember the longest term that matches the query *exactly*
Object.keys(match.matchData.metadata).forEach(term => {
+ if (
+ query.toLowerCase().includes(term.toLowerCase()) &&
+ longestTerm.length < term.length
+ ) {
+ longestTerm = term;
+ }
+
Object.keys(match.matchData.metadata[term]).forEach(fieldName => {
const { position: positions } = match.matchData.metadata[term][fieldName];
highlights[fieldName] = [...(highlights[fieldName] || []), ...positions];
});
});
- return { page, highlights };
+ return { page, highlights, longestTerm };
})
.filter(result => result.page) as SearchResult[];
+ // re-order results by the length of the longest matched term
+ // the longer term is the more chances the result is more relevant
+ const sortedResults = sortBy(results, result => -result.longestTerm.length);
+
return (
<>
- {results.map(result => (
+ {sortedResults.map(result => (
<SearchResultEntry
active={result.page.relativeName === this.props.splat}
key={result.page.relativeName}
describe('SearchResultEntry', () => {
it('should render', () => {
expect(
- shallow(<SearchResultEntry active={true} result={{ page, highlights: {} }} />)
+ shallow(
+ <SearchResultEntry active={true} result={{ page, highlights: {}, longestTerm: '' }} />
+ )
).toMatchSnapshot();
});
});
describe('SearchResultText', () => {
it('should render with highlights', () => {
expect(
- shallow(<SearchResultText result={{ page, highlights: { text: [[12, 9]] } }} />)
+ shallow(
+ <SearchResultText result={{ page, highlights: { text: [[12, 9]] }, longestTerm: '' }} />
+ )
).toMatchSnapshot();
});
it('should render without highlights', () => {
- expect(shallow(<SearchResultText result={{ page, highlights: {} }} />)).toMatchSnapshot();
+ expect(
+ shallow(<SearchResultText result={{ page, highlights: {}, longestTerm: '' }} />)
+ ).toMatchSnapshot();
});
});
describe('SearchResultTitle', () => {
it('should render with highlights', () => {
expect(
- shallow(<SearchResultTitle result={{ page, highlights: { title: [[0, 6]] } }} />)
+ shallow(
+ <SearchResultTitle result={{ page, highlights: { title: [[0, 6]] }, longestTerm: '' }} />
+ )
).toMatchSnapshot();
});
it('should render not without highlights', () => {
- expect(shallow(<SearchResultTitle result={{ page, highlights: {} }} />)).toMatchSnapshot();
+ expect(
+ shallow(<SearchResultTitle result={{ page, highlights: {}, longestTerm: '' }} />)
+ ).toMatchSnapshot();
});
});
result={
Object {
"highlights": Object {},
+ "longestTerm": "",
"page": Object {
"content": "",
"order": -1,
result={
Object {
"highlights": Object {},
+ "longestTerm": "",
"page": Object {
"content": "",
"order": -1,
],
],
},
+ "longestTerm": "from",
"page": Object {
"content": "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words.",
"order": -1,
],
],
},
+ "longestTerm": "from",
"page": Object {
"content": "Foobar is a universal variable understood to represent whatever is being discussed.",
"order": -1,