Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Desktop: Add option to select the search result order #7622

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Expand Up @@ -683,6 +683,7 @@ packages/lib/services/rest/utils/readonlyProperties.js
packages/lib/services/rest/utils/requestFields.js
packages/lib/services/rest/utils/requestPaginationOptions.js
packages/lib/services/searchengine/SearchEngine.js
packages/lib/services/searchengine/SearchEngine.test.js
packages/lib/services/searchengine/SearchEngineUtils.js
packages/lib/services/searchengine/SearchEngineUtils.test.js
packages/lib/services/searchengine/SearchFilter.test.js
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -671,6 +671,7 @@ packages/lib/services/rest/utils/readonlyProperties.js
packages/lib/services/rest/utils/requestFields.js
packages/lib/services/rest/utils/requestPaginationOptions.js
packages/lib/services/searchengine/SearchEngine.js
packages/lib/services/searchengine/SearchEngine.test.js
packages/lib/services/searchengine/SearchEngineUtils.js
packages/lib/services/searchengine/SearchEngineUtils.test.js
packages/lib/services/searchengine/SearchFilter.test.js
Expand Down
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -473,8 +473,8 @@ The filters are implicitly connected by and/or connectives depending on the foll
Incorrect search filters are interpreted as a phrase search, e.g. misspelled `nootebook:Example` or non-existing `https://joplinapp.org`.

## Search order

Notes are sorted by "relevance". Currently it means the notes that contain the requested terms the most times are on top. For queries with multiple terms, it also matters how close to each other the terms are. This is a bit experimental so if you notice a search query that returns unexpected results, please report it in the forum, providing as many details as possible to replicate the issue.
Search results can be sorted by "relevance", title creation date or modification date.
The "relevance" (BM25) sorting means the notes that contain the requested terms the most times are on top. For queries with multiple terms, it also matters how close to each other the terms are. This is a bit experimental so if you notice a search query that returns unexpected results, please report it in the forum, providing as many details as possible to replicate the issue.

# Goto Anything

Expand Down
13 changes: 13 additions & 0 deletions packages/app-desktop/gui/MenuBar.tsx
Expand Up @@ -113,8 +113,10 @@ interface Props {
selectedFolderId: string;
layoutButtonSequence: number;
['notes.sortOrder.field']: string;
['search.sortOrder.field']: string;
['folders.sortOrder.field']: string;
['notes.sortOrder.reverse']: boolean;
['search.sortOrder.reverse']: boolean;
['folders.sortOrder.reverse']: boolean;
showNoteCounts: boolean;
uncompletedTodosOnTop: boolean;
Expand Down Expand Up @@ -179,6 +181,7 @@ function useMenuStates(menu: any, props: Props) {
}

applySortItemCheckState('notes');
applySortItemCheckState('search');
applySortItemCheckState('folders');

menuItemSetChecked('showNoteCounts', props.showNoteCounts);
Expand All @@ -199,10 +202,14 @@ function useMenuStates(menu: any, props: Props) {
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
props['notes.sortOrder.field'],
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
props['search.sortOrder.field'],
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
props['folders.sortOrder.field'],
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
props['notes.sortOrder.reverse'],
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
props['search.sortOrder.reverse'],
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
props['folders.sortOrder.reverse'],
props.showNoteCounts,
props.uncompletedTodosOnTop,
Expand Down Expand Up @@ -360,6 +367,7 @@ function useMenu(props: Props) {
};

const sortNoteItems = sortNoteFolderItems('notes');
const sortNoteSearch = sortNoteFolderItems('search');
const sortFolderItems = sortNoteFolderItems('folders');

const focusItems = [
Expand Down Expand Up @@ -683,6 +691,9 @@ function useMenu(props: Props) {
{
label: Setting.settingMetadata('notes.sortOrder.field').label(),
submenu: sortNoteItems,
}, {
label: Setting.settingMetadata('search.sortOrder.field').label(),
submenu: sortNoteSearch,
}, {
label: Setting.settingMetadata('folders.sortOrder.field').label(),
submenu: sortFolderItems,
Expand Down Expand Up @@ -975,8 +986,10 @@ const mapStateToProps = (state: AppState) => {
selectedFolderId: state.selectedFolderId,
layoutButtonSequence: state.settings.layoutButtonSequence,
['notes.sortOrder.field']: state.settings['notes.sortOrder.field'],
['search.sortOrder.field']: state.settings['search.sortOrder.field'],
['folders.sortOrder.field']: state.settings['folders.sortOrder.field'],
['notes.sortOrder.reverse']: state.settings['notes.sortOrder.reverse'],
['search.sortOrder.reverse']: state.settings['search.sortOrder.reverse'],
['folders.sortOrder.reverse']: state.settings['folders.sortOrder.reverse'],
showNoteCounts: state.settings.showNoteCounts,
uncompletedTodosOnTop: state.settings.uncompletedTodosOnTop,
Expand Down
1 change: 1 addition & 0 deletions packages/app-desktop/gui/NoteList/NoteList.tsx
Expand Up @@ -528,6 +528,7 @@ const mapStateToProps = (state: AppState) => {
provisionalNoteIds: state.provisionalNoteIds,
isInsertingNotes: state.isInsertingNotes,
noteSortOrder: state.settings['notes.sortOrder.field'],
searchSortOrder: state.settings['search.sortOrder.field'],
highlightedWords: state.highlightedWords,
plugins: state.pluginService.plugins,
customCss: state.customCss,
Expand Down
Expand Up @@ -11,6 +11,14 @@ export const notesSortOrderFieldArray = (): string[] => {
return fields;
};

export const searchSortOrderFieldArray = (): string[] => {
// The order of the fields is strictly determinate.
if (fields === null) {
fields = Setting.enumOptionValues('search.sortOrder.field').sort().reverse();
}
return fields;
};

export const notesSortOrderNextField = (currentField: string) => {
const fields = notesSortOrderFieldArray();
const index = fields.indexOf(currentField);
Expand Down
1 change: 1 addition & 0 deletions packages/lib/models/Note.ts
Expand Up @@ -32,6 +32,7 @@ export default class Note extends BaseItem {

static fieldToLabel(field: string) {
const fieldsToLabels: Record<string, string> = {
bm25: _('relevance'),
title: _('title'),
user_updated_time: _('updated date'),
user_created_time: _('created date'),
Expand Down
21 changes: 21 additions & 0 deletions packages/lib/models/Setting.ts
Expand Up @@ -919,6 +919,26 @@ class Setting extends BaseModel {
storage: SettingStorage.File,
isGlobal: true,
},
'search.sortOrder.field': {
value: 'bm25',
type: SettingItemType.String,
section: 'note',
isEnum: true,
public: true,
appTypes: [AppType.Cli, AppType.Desktop],
label: () => _('Sort search by'),
options: () => {
const Note = require('./Note').default;
const noteSortFields = ['bm25', 'user_updated_time', 'user_created_time', 'title'];
const options: any = {};
for (let i = 0; i < noteSortFields.length; i++) {
options[noteSortFields[i]] = toTitleCase(Note.fieldToLabel(noteSortFields[i]));
}
return options;
},
storage: SettingStorage.File,
isGlobal: true,
},
'editor.autoMatchingBraces': {
value: true,
type: SettingItemType.Bool,
Expand All @@ -930,6 +950,7 @@ class Setting extends BaseModel {
isGlobal: true,
},
'notes.sortOrder.reverse': { value: true, type: SettingItemType.Bool, storage: SettingStorage.File, isGlobal: true, section: 'note', public: true, label: () => _('Reverse sort order'), appTypes: [AppType.Cli] },
'search.sortOrder.reverse': { value: false, type: SettingItemType.Bool, storage: SettingStorage.File, isGlobal: true, section: 'note', public: true, label: () => _('Reverse sort order'), appTypes: [AppType.Cli] },
// NOTE: A setting whose name starts with 'notes.sortOrder' is special,
// which implies changing the setting automatically triggers the refresh of notes.
// See lib/BaseApplication.ts/generalMiddleware() for details.
Expand Down