Evaluate AppDatabase-backed Bases query execution for large vault tables instead of renderer-only... #4
Labels
No labels
abandoned
active
audit
blocked
data-safety
difficulty:easy
difficulty:hard
difficulty:high
difficulty:medium
docs
done
duplicate
notebook-v0
open
priority:high
ready
release-critical
safe-mode
spec
spec-backlog
subsystem:api
subsystem:backlog
subsystem:bases
subsystem:ci
subsystem:command
subsystem:configuration
subsystem:consolidate
subsystem:dependencies
subsystem:desktop-electron
subsystem:diffmerge
subsystem:docker
subsystem:docs
subsystem:fuzzy
subsystem:graph
subsystem:hotkeys
subsystem:lapis
subsystem:maint
subsystem:maintenance
subsystem:markdown
subsystem:markdown-lint
subsystem:md018
subsystem:notebook
subsystem:notifications
subsystem:opfs
subsystem:package
subsystem:plugin-markdown
subsystem:plugin-tasks
subsystem:plugins
subsystem:registry
subsystem:release
subsystem:renovate
subsystem:restore
subsystem:scripts
subsystem:search
subsystem:settings
subsystem:spec
subsystem:tasks
subsystem:testing
subsystem:ui
subsystem:web
subsystem:workspace
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lapis-notes/lapis#4
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Evaluate and implement an AppDatabase-backed query path for Bases views over large vaults instead of relying on renderer-only metadata iteration.
Problem
Bases views currently build their query input in the renderer from
app.metadataCache.fileCache. Each openQueryControllerturns the full metadata cache into aVaultRecord[], askscolumnsFor(...)to scan that row set, creates a PEaQLBasesTable, and executes generated SQL synchronously over the renderer-owned array.That preserves current
.baseand.basessemantics, but it scales poorly for large vault tables because the renderer repeatedly performs vault-wide row construction, column inference, filtering, sorting, grouping, and formula evaluation. The app already persists generated file, metadata, link, tag, and property state behindAppDatabase; Bases should evaluate whether that generated-state layer can provide a narrower, off-renderer query path without leaking plugin internals intopackages/api.Goal
Deliver a correctness-preserving AppDatabase-backed Bases query path that reduces renderer work for large vault tables while keeping the existing Bases document model, PEaQL-facing semantics, custom view data shape, and markdown-hosted read-only surfaces intact.
The preferred result is a staged implementation: use
AppDatabaseto load or narrow candidate rows, keep PEaQL ownership inplugin-bases, and fall back to the current in-memory evaluator whenever a query shape cannot be safely lowered.Scope
SqliteWasmAppDatabaseCore, the worker proxy, and the coordinated browser proxy.VaultRecord/BasesEntryshape.QueryControllerreloads from synchronous derived array execution to async, generation-tokened reloads that cannot overwrite newer document or metadata changes with stale database responses.base/basesblocks.Non-goals
.baseor.basesfile format.AppDatabase.plugin-basesopen SQLite, native stores, or browser storage directly.packages/api.Current State
QueryController.datais derived fromapp.metadataCache.fileCacheand maps every cached file into aVaultRecord.columnsFor(...)infers frontmatter columns by scanning the current renderer row set, while also materializing explicitnote.*properties declared in a Bases document.QueryController.dbcreates a PEaQLContextovernew BasesTable("bases").withPropertyColumns(...).data(() => this.data).QueryController.getRecords(...)callsthis.db.execute(qs)synchronously and converts the result into the existingBasesEntry/BasesQueryResultpipeline.AppDatabasealready persistsfiles,metadata,links,tags, andproperties, but the public contract currently exposes write operations for indexed metadata and does not expose a Bases-suitable read/query surface.NativeDesktopAppDatabasecurrently persists serialized generated state and mirrors search documents into native search tables, but it does not expose a dedicated metadata query IPC surface.QueryControllerand uses dependency tracking over document filters, view filters, visible/sorted properties, group-by state, formulas, and direct file-reference neighborhoods.Implementation Details
1. Characterize current behavior
inFolder, explicitnote.*fields, inferred frontmatter fields, tags, links, sort, limit, grouping, formulas, and search-within-view.2. Design the AppDatabase metadata read contract
Add serializable types in
packages/apifor a plugin-neutral indexed metadata query surface. The contract should expose data that any structured metadata consumer could use, not Bases-specific PEaQL concepts.Suggested shape:
The exact API can differ, but it should:
VaultRecordfor existing Bases rendering.plugin-bases, PEaQL, or Svelte concepts intopackages/api.3. Implement AppDatabase backends
AppDatabaseinterface.MemoryAppDatabaseby reading the existing in-memoryfiles,metadata,properties,tags, andlinksmaps.IndexedDbAppDatabaseinherit the memory behavior after opening/restoring state, unless a storage-specific read path is necessary.SqliteWasmAppDatabaseCoreusing the existingfiles,metadata,properties,tags, andlinkstables.(path, name),(name, value_json), or file extension/path indexes, without overfitting to one Bases view.sqlite-wasm-app-database.worker.ts,SqliteWasmAppDatabase, andBrowserCoordinatedAppDatabaseso browser worker and secondary-tab behavior stays consistent.NativeDesktopAppDatabasecan answer from restored serialized state or needs a native IPC query. If native IPC is required, add a narrow host command that returns the same serializable row shape.4. Add a Bases query-source abstraction
Introduce a small boundary inside
packages/plugins/plugin-bases, for example:Responsibilities:
TFileorTFile-compatible file objects from returned file records by resolving paths throughapp.vault.getFileByPath(...)when available and using the existing fallbackTFileconstruction otherwise.CachedMetadata/frontmatter-compatible row data from returned metadata and property records.BasesEntryidentity and custom views continue to work.5. Convert QueryController reloads to async
dataderived value and directthis.db.execute(...)reload path with an explicit async reload pipeline.loaded, metadatachanged, and metadatadeletedevents working.view.onDataUpdated()only when the active view receives a freshBasesQueryResult.6. Lower safe constraints first
Start with conservative candidate narrowing rather than full SQL execution of every PEaQL expression.
Good first candidates:
file.path,file.name,file.folder, andinFolderpath-prefix filters.tagstable.linkstable.Fallback cases should continue through the current PEaQL path:
7. Preserve column inference and explicit note properties
note.*properties declared in the Bases document even when the current metadata rows do not contain that frontmatter key.frontMatterTypesForColumns(...)still supplies PEaQL property column types for both explicit and inferred note fields..basedocuments that reference structured fields such asnote.statusbefore any note currently carries that property.8. Update invalidation behavior
shouldReloadForMetadataChange(...)dependency logic.loaded,changed, anddeletedevents.MetadataCachewritesupsertIndexedFile(...),deleteIndexedFile(...), orrenameIndexedFile(...).9. Document the result
Update:
spec/src/20-packages/plugins/bases/index.mdspec/src/30-cross-package-contracts/storage-metadata-search.mdpackages/plugins/plugin-bases/spec.mdDocument:
AppDatabase.Acceptance Criteria
AppDatabaseinstead of always rebuilding a full renderer-ownedVaultRecord[]frommetadataCache.fileCache.note.*fields declared in a Bases document continue to compile and render even when no current row has that frontmatter key.BasesQueryResultshape they receive today.base/basescode blocks continue to render in read-only mode.Suggested Files or Specs To Inspect
packages/api/src/lib/storage/app-database.tspackages/api/src/lib/storage/sqlite-wasm-app-database-core.tspackages/api/src/lib/storage/sqlite-wasm-app-database.tspackages/api/src/lib/storage/sqlite-wasm-app-database.worker.tspackages/api/src/lib/storage/browser-coordinated-app-database.tspackages/api/src/lib/storage/desktop-native.tspackages/api/src/lib/cache.svelte.tspackages/plugins/plugin-bases/src/bases-view/bases.svelte.tspackages/plugins/plugin-bases/src/bases-view/db.tspackages/plugins/plugin-bases/src/bases-view/columns.tspackages/plugins/plugin-bases/src/bases-view/filter-parser.tspackages/plugins/plugin-bases/src/bases-view/file-fields-core.tspackages/plugins/plugin-bases/spec.mdspec/src/20-packages/plugins/bases/index.mdspec/src/30-cross-package-contracts/storage-metadata-search.mdTest Plan
AppDatabase tests
Bases tests
note.*properties are materialized without observed frontmatter values.loaded,changed, anddeletedevents trigger correct reloads.BasesQueryResultdata.Manual or smoke checks
Validation Commands
pnpm --filter @lapis-notes/api test:run src/lib/__tests__/app-database.test.tspnpm --filter @lapis-notes/bases test -- src/bases-viewpnpm --filter @lapis-notes/api checkpnpm --filter @lapis-notes/bases checkpnpm checkmake spec-lintmdbook build specpnpm test:smokeCompletion Notes
When implementation and validation are complete, close this issue through the backlog helper with a concise implementation summary and commit with
Fixes #4in the commit message body.