quintodrome/db/migrations
Deluan Quintão 54de0dbc52
feat(server): implement FTS5-based full-text search (#5079)
* build: add sqlite_fts5 build tag to enable FTS5 support

* feat: add SearchBackend config option (default: fts)

* feat: add buildFTS5Query for safe FTS5 query preprocessing

* feat: add FTS5 search backend with config toggle, refactor legacy search

- Add searchExprFunc type and getSearchExpr() for backend selection
- Rename fullTextExpr to legacySearchExpr
- Add ftsSearchExpr using FTS5 MATCH subquery
- Update fullTextFilter in sql_restful.go to use configured backend

* feat: add FTS5 migration with virtual tables, triggers, and search_participants

Creates FTS5 virtual tables for media_file, album, and artist with
unicode61 tokenizer and diacritic folding. Adds search_participants
column, populates from JSON, and sets up INSERT/UPDATE/DELETE triggers.

* feat: populate search_participants in PostMapArgs for FTS5 indexing

* test: add FTS5 search integration tests

* fix: exclude FTS5 virtual tables from e2e DB restore

The restoreDB function iterates all tables in sqlite_master and
runs DELETE + INSERT to reset state. FTS5 contentless virtual tables
cannot be directly deleted from. Since triggers handle FTS5 sync
automatically, simply skip tables matching *_fts and *_fts_* patterns.

* build: add compile-time guard for sqlite_fts5 build tag

Same pattern as netgo: compilation fails with a clear error if
the sqlite_fts5 build tag is missing.

* build: add sqlite_fts5 tag to reflex dev server config

* build: extract GO_BUILD_TAGS variable in Makefile to avoid duplication

* fix: strip leading * from FTS5 queries to prevent "unknown special query" error

* feat: auto-append prefix wildcard to FTS5 search tokens for broader matching

Every plain search token now gets a trailing * appended (e.g., "love" becomes
"love*"), so searching for "love" also matches "lovelace", "lovely", etc.
Quoted phrases are preserved as exact matches without wildcards. Results are
ordered alphabetically by name/title, so shorter exact matches naturally
appear first.

* fix: clarify comments about FTS5 operator neutralization

The comments said "strip" but the code lowercases operators to
neutralize them (FTS5 operators are case-sensitive). Updated comments
to accurately describe the behavior.

* fix: use fmt.Sprintf for FTS5 phrase placeholders

The previous encoding used rune('0'+index) which silently breaks with
10+ quoted phrases. Use fmt.Sprintf for arbitrary index support.

* fix: validate and normalize SearchBackend config option

Normalize the value to lowercase and fall back to "fts" with a log
warning for unrecognized values. This prevents silent misconfiguration
from typos like "FTS", "Legacy", or "fts5".

* refactor: improve documentation for build tags and FTS5 requirements

Signed-off-by: Deluan <deluan@navidrome.org>

* refactor: convert FTS5 query and search backend normalization tests to DescribeTable format

Signed-off-by: Deluan <deluan@navidrome.org>

* fix: add sqlite_fts5 build tag to golangci configuration

Signed-off-by: Deluan <deluan@navidrome.org>

* feat: add UISearchDebounceMs configuration option and update related components

Signed-off-by: Deluan <deluan@navidrome.org>

* fix: fall back to legacy search when SearchFullString is enabled

FTS5 is token-based and cannot match substrings within words, so
getSearchExpr now returns legacySearchExpr when SearchFullString
is true, regardless of SearchBackend setting.

* fix: add sqlite_fts5 build tag to CI pipeline and Dockerfile

* fix: add WHEN clauses to FTS5 AFTER UPDATE triggers

Added WHEN clauses to the media_file_fts_au, album_fts_au, and
artist_fts_au triggers so they only fire when FTS-indexed columns
actually change. Previously, every row update (e.g., play count, rating,
starred status) triggered an unnecessary delete+insert cycle in the FTS
shadow tables. The WHEN clauses use IS NOT for NULL-safe comparison of
each indexed column, avoiding FTS index churn for non-indexed updates.

* feat: add SearchBackend configuration option to data and insights components

Signed-off-by: Deluan <deluan@navidrome.org>

* fix: enhance input sanitization for FTS5 by stripping additional punctuation and special characters

Signed-off-by: Deluan <deluan@navidrome.org>

* feat: add search_normalized column for punctuated name search (R.E.M., AC/DC)

Add index-time normalization and query-time single-letter collapsing to
fix FTS5 search for punctuated names. A new search_normalized column
stores concatenated forms of punctuated words (e.g., "R.E.M." → "REM",
"AC/DC" → "ACDC") and is indexed in FTS5 tables. At query time, runs of
consecutive single letters (from dot-stripping) are collapsed into OR
expressions like ("R E M" OR REM*) to match both the original tokens and
the normalized form. This enables searching by "R.E.M.", "REM", "AC/DC",
"ACDC", "A-ha", or "Aha" and finding the correct results.

* refactor: simplify isSingleUnicodeLetter to avoid []rune allocation

Use utf8.DecodeRuneInString to check for a single Unicode letter
instead of converting the entire string to a []rune slice.

* feat: define ftsSearchColumns for flexible FTS5 search column inclusion

Signed-off-by: Deluan <deluan@navidrome.org>

* feat: update collapseSingleLetterRuns to return quoted phrases for abbreviations

Signed-off-by: Deluan <deluan@navidrome.org>

* feat: implement extractPunctuatedWords to handle artist/album names with embedded punctuation

Signed-off-by: Deluan <deluan@navidrome.org>

* feat: implement extractPunctuatedWords to handle artist/album names with embedded punctuation

Signed-off-by: Deluan <deluan@navidrome.org>

* refactor: punctuated word handling to improve processing of artist/album names

Signed-off-by: Deluan <deluan@navidrome.org>

* feat: add CJK support for search queries with LIKE filters

Signed-off-by: Deluan <deluan@navidrome.org>

* feat: enhance FTS5 search by adding album version support and CJK handling

Signed-off-by: Deluan <deluan@navidrome.org>

* refactor: search configuration to use structured options

Signed-off-by: Deluan <deluan@navidrome.org>

* feat: enhance search functionality to support punctuation-only queries and update related tests

Signed-off-by: Deluan <deluan@navidrome.org>

---------

Signed-off-by: Deluan <deluan@navidrome.org>
2026-02-21 17:52:42 -05:00
..
20200130083147_create_schema.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200131183653_standardize_item_type.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200208222418_add_defaults_to_annotations.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200220143731_change_duration_to_float.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200310171621_enable_search_by_albumartist.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200310181627_add_transcoding_and_player_tables.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200319211049_merge_search_into_main_tables.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200325185135_add_album_artist_id.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200326090707_fix_album_artists_importing.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200327193744_add_year_range_to_album.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200404214704_add_indexes.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200409002249_enable_search_by_tracks_artists.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200411164603_add_created_and_updated_fields_to_playlists.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200418110522_reindex_to_fix_album_years.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200419222708_reindex_to_change_full_text_search.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200423204116_add_sort_fields.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200508093059_add_artist_song_count.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200512104202_add_disc_subtitle.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200516140647_add_playlist_tracks_table.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200608153717_referential_integrity.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200706231659_add_default_transcodings.go feat(bfr): Big Refactor: new scanner, lots of new fields and tags, improvements and DB schema changes (#2709) 2025-02-19 20:35:17 -05:00
20200710211442_add_playlist_path.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200731095603_create_play_queues_table.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200801101355_create_bookmark_table.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20200819111809_drop_email_unique_constraint.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20201003111749_add_starred_at_index.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20201010162350_add_album_size.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20201012210022_add_artist_playlist_size.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20201021085410_add_mbids.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20201021093209_add_media_file_indexes.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20201021135455_add_media_file_artist_index.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20201030162009_add_artist_info_table.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20201110205344_add_comments_and_lyrics.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20201128100726_add_real-path_option.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20201213124814_add_all_artist_ids_to_album.go Refactor string utilities into its own package str 2024-06-05 22:09:27 -04:00
20210322132848_add_timestamp_indexes.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20210418232815_fix_album_comments.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20210430212322_add_bpm_metadata.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20210530121921_create_shares_table.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20210601231734_update_share_fieldnames.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20210616150710_encrypt_all_passwords.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20210619231716_drop_player_name_unique_constraint.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20210623155401_add_user_prefs_player_scrobbler_enabled.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20210625223901_add_referential_integrity_to_user_props.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20210626213026_add_scrobble_buffer.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20210715151153_add_genre_tables.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20210821212604_add_mediafile_channels.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20211008205505_add_smart_playlist.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20211023184825_add_order_title_to_media_file.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20211026191915_unescape_lyrics_and_comments.go Refactor string utilities into its own package str 2024-06-05 22:09:27 -04:00
20211029213200_add_userid_to_playlist.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20211102215414_add_alphabetical_by_artist_index.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20211105162746_remove_invalid_artist_ids.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20220724231849_add_musicbrainz_release_track_id.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20221219112733_add_album_image_paths.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20221219140528_remove_cover_art_id.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20230112111457_add_album_paths.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20230114121537_touch_playlists.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20230115103212_create_internet_radio.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20230117155559_add_replaygain_metadata.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20230117180400_add_album_info.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20230119152657_recreate_share_table.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20230202143713_change_path_list_separator.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20230209181414_change_image_files_list_separator.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20230310222612_add_download_to_share.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20230404104309_empty_sql_migration.sql Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20230515184510_add_release_date.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20230616214944_rename_musicbrainz_recording_id.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20231208182311_add_discs_to_album.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20231209211223_alter_lyric_column.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20240122223340_add_default_values_to_null_columns.go.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20240426202913_add_id_to_scrobble_buffer.go Fix migration package name mismatch 2024-05-08 19:54:48 -04:00
20240511210036_add_sample_rate.go Add sampleRate to the DB 2024-05-11 17:57:45 -04:00
20240511220020_add_library_table.go feat(bfr): Big Refactor: new scanner, lots of new fields and tags, improvements and DB schema changes (#2709) 2025-02-19 20:35:17 -05:00
20240629152843_remove_annotation_id.go Remove unnecessary annotation table primary key 2024-06-29 11:45:41 -04:00
20240802044339_player_use_user_id_over_username.go Use userId in player, other fixes (#3182) 2024-08-03 13:37:21 -04:00
20241020003138_add_sort_tags_index.sql perf(server): add index for sort tags. 2024-10-19 20:46:54 -04:00
20241024125533_fix_sort_tags_collation_and_index.sql fix(server): fix case-insensitive sort order and add indexes to improve performance (#3425) 2024-10-26 14:06:34 -04:00
20241026183640_support_new_scanner.go refactor: rename chain package to run and update references 2025-06-14 17:19:06 -04:00
20250522013904_share_user_id_on_delete.sql fix(db): allow deleting users that have shares (#4098) 2025-05-21 22:16:10 -04:00
20250611010101_playqueue_current_to_index.go feat(server): add index-based play queue endpoints to native API (#4210) 2025-06-10 23:00:44 -04:00
20250701010101_add_folder_hash.go feat(scanner): add folder hash for smarter quick scan change detection (#4220) 2025-06-12 13:17:34 -04:00
20250701010102_add_annotation_user_foreign_key.sql fix(db): add user foreign key constraint to annotation table (#4211) 2025-06-13 17:27:57 -04:00
20250701010103_add_library_stats.go fix(scanner): filter folders by num_audio_files to ensure accurate statistics 2025-06-23 10:26:26 -04:00
20250701010104_make_replaygain_fields_nullable.go fix: Allow nullable ReplayGain and support 0.0 (#4239) 2025-06-17 12:02:25 -04:00
20250701010105_remove_dangling_items.sql fix(db): clear dangling music from BFR upgrade (#4262) 2025-06-28 18:43:11 -04:00
20250701010106_add_participant_stats_to_all_artists.sql feat(server): expose main credit stat to reflect only album artist | artist credit (#4268) 2025-06-28 19:00:13 -04:00
20250701010107_add_mbid_indexes.sql feat: MBID search functionality for albums, artists and songs (#4286) 2025-06-30 17:11:54 -04:00
20250701010108_add_multi_library_support.go feat: Multi-library support (#4181) 2025-07-18 18:41:12 -04:00
20250823142158_make_playqueue_position_int.sql fix(db): make playqueue position field an integer (#4481) 2025-11-06 14:41:09 -05:00
20251109010105_add_annotation_rating_date.sql feat: add Rated At field - #4653 (#4660) 2025-11-24 23:18:05 -05:00
20251206013022_create_scrobbles_table.sql feat(server): track scrobble/linstens history (#4770) 2025-12-06 11:07:18 -05:00
20260104203627_playlist_case_insensitive_name.sql fix(ui): make playlist name sorting case-insensitive (#4845) 2026-01-05 19:05:11 -05:00
20260106000620_create_plugin_table.sql feat(plugins): New Plugin System with multi-language PDK support (#4833) 2026-01-14 19:22:48 -05:00
20260117201522_add_avg_rating_column.sql feat(subsonic): Add avgRating from subsonic spec (#4900) 2026-01-18 17:42:42 -05:00
20260220173400_add_fts5_search.go feat(server): implement FTS5-based full-text search (#5079) 2026-02-21 17:52:42 -05:00
migration.go feat: add DevOptimizeDB flag to control SQLite optimization 2025-11-25 19:49:03 -05:00