Put a keyword on a photograph, not only search for one
The catalog has been able to *find* by keyword since v1 — query.rs joins the keywords table, matches it exactly, and substring-matches it for free text — and nothing anywhere could ever put a word there. A user could filter to a keyword they had no way to apply. This is the missing half: create, rename, delete, list, assign, unassign, and the two reads a panel needs. Bulk-only for assignment, because keywording a selection is the common case rather than the exception — the photographer picks out the frames with the puffin in them and applies "puffin" once, in one transaction. Schema v6 adds `keyword_terms`, and deliberately does *not* touch the v1 join. The assignment keeps the word as text because the catalog is a rebuildable index and the durable copies of that fact — the sidecar, XMP dc:subject — both carry a string; a foreign key would mean a catalog rebuilt from sidecars had to invent identity rows before it could record anything, and would break the query path that already works. So the text is the fact, and the new table is only the identity a rename and a deletion can be keyed on. `keyword_terms.name` carries no unique index, which looks like an oversight and is not: two devices that each type "Iceland" are both right until they meet, and a constraint would abort the merge at that moment. Uniqueness is converged upon instead — create resolves an existing name, fuse_duplicates collapses a cross-device pair onto the smaller uuid. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,9 +14,10 @@
|
||||
//! - [`walk`] — those decisions driven against real storage, local or SAF
|
||||
//! - [`query`] — selectors compiled to indexed SQL, windowed for the grid
|
||||
//! - [`collections`] — the collection tree and membership the UI edits
|
||||
//! - [`keywords`] — the keyword vocabulary and what it is assigned to
|
||||
//! - [`jobs`] — the durable background work queue
|
||||
//! - [`trash`] — soft delete to a folder, then permanent delete
|
||||
//! - [`merge`] / [`sync`] — cross-device collection merging
|
||||
//! - [`merge`] / [`sync`] — cross-device merging of collections and keywords
|
||||
//!
|
||||
//! # The one thing everything is designed around
|
||||
//!
|
||||
@@ -35,6 +36,7 @@ pub mod cache;
|
||||
pub mod collections;
|
||||
pub mod error;
|
||||
pub mod jobs;
|
||||
pub mod keywords;
|
||||
pub mod merge;
|
||||
pub mod query;
|
||||
pub mod rating;
|
||||
@@ -48,6 +50,7 @@ pub use cache::{Budget, Cache, DEFAULT_BUDGET_BYTES};
|
||||
pub use collections::{Collection, CollectionKind, TreeRow};
|
||||
pub use error::CatalogError;
|
||||
pub use jobs::{Job, JobKind, Priority};
|
||||
pub use keywords::{Coverage, Keyword, KeywordId, SelectionKeyword};
|
||||
pub use merge::MergeReport;
|
||||
pub use query::{Query, Sort};
|
||||
pub use rating::{Judgement, MAX_RATING};
|
||||
|
||||
Reference in New Issue
Block a user