dirsql 0.4.30__tar.gz → 0.4.32__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dirsql-0.4.30 → dirsql-0.4.32}/Cargo.lock +2 -2
- {dirsql-0.4.30 → dirsql-0.4.32}/PKG-INFO +1 -1
- {dirsql-0.4.30/packages/rust → dirsql-0.4.32}/docs/howto/search-by-meaning.md +41 -19
- {dirsql-0.4.30/packages/rust → dirsql-0.4.32}/docs/howto/search-indexes.md +27 -2
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/reference/cli.md +44 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/reference/sdk.md +19 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/Cargo.toml +1 -1
- {dirsql-0.4.30 → dirsql-0.4.32/packages/python}/docs/howto/search-by-meaning.md +41 -19
- {dirsql-0.4.30 → dirsql-0.4.32/packages/python}/docs/howto/search-indexes.md +27 -2
- {dirsql-0.4.30/packages/rust → dirsql-0.4.32/packages/python}/docs/reference/cli.md +44 -0
- {dirsql-0.4.30/packages/rust → dirsql-0.4.32/packages/python}/docs/reference/sdk.md +19 -0
- dirsql-0.4.32/packages/python/e2e-attestations/claude-tackle-957-lrm0z6.json +7 -0
- dirsql-0.4.32/packages/python/e2e-attestations/claude-tackle-975-n8mc2h.json +7 -0
- dirsql-0.4.32/packages/python/testing-conventions.toml +34 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/Cargo.toml +1 -1
- {dirsql-0.4.30/packages/python → dirsql-0.4.32/packages/rust}/docs/howto/search-by-meaning.md +41 -19
- {dirsql-0.4.30/packages/python → dirsql-0.4.32/packages/rust}/docs/howto/search-indexes.md +27 -2
- {dirsql-0.4.30/packages/python → dirsql-0.4.32/packages/rust}/docs/reference/cli.md +44 -0
- {dirsql-0.4.30/packages/python → dirsql-0.4.32/packages/rust}/docs/reference/sdk.md +19 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/lib.rs +83 -14
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/matcher.rs +48 -0
- dirsql-0.4.32/packages/rust/src/progress.rs +590 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/scanner.rs +24 -0
- dirsql-0.4.30/packages/python/testing-conventions.toml +0 -15
- {dirsql-0.4.30 → dirsql-0.4.32}/Cargo.toml +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/README.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/__init__.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/_async.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/_dirsql.pyi +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/cli/__init__.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/cli/discover_plugins/__init__.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/cli/discover_plugins/discovered_fragments.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/cli/discover_plugins/discovery_disabled.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/cli/discover_plugins/fragment_path.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/cli/discover_plugins/user_passed_config.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/cli/discover_plugins/with_discovered_plugins.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/cli/main.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/cli/resolve_config_extensions.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/py.typed +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/resolve_config_extensions.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/dirsql/resolve_extension.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/.vitepress/config.ts +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/AGENTS.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/explanation.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/getting-started.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/howto/embed.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/howto/persist.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/howto/query-json.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/index.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/package.json +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/plugins.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/reference/columns.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/reference/config.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/reference/hooks.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/reference/http-api.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/CHANGELOG.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/MIGRATIONS.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/README.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-07-13-plugin-discovery.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-07-13-repeatable-config.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-07-29-restore-python-310.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-08-02-release-profile.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-08-02-scan-failures.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-08-03-discovery-ext-resolution.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-08-03-manylinux-dynamic-binary.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-08-03-python-no-ignore.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-08-04-cast-lints.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-08-04-cli-in-process.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-08-20-curtaincall-dev-dependency.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/2026-08-20-declared-table-name.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/changelog.d/README.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/conftest.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/.vitepress/config.ts +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/AGENTS.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/explanation.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/getting-started.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/howto/embed.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/howto/persist.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/howto/query-json.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/index.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/package.json +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/plugins.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/reference/columns.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/reference/config.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/reference/hooks.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/reference/http-api.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/e2e-attestations/cc-keen-knuth-f11gyq.json +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/e2e-attestations/claude-819-oneshot-timeout.json +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/e2e-attestations/claude-827-unquote-doubled-quotes.json +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/e2e-attestations/claude-epic-953-slice2-reedline.json +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/e2e-attestations/claude-epic-953-slice3-format.json +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/e2e-attestations/claude-epic-953-stacked-prs-x59wm3.json +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/e2e-attestations/claude-github-issue-825-e4mhhp.json +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/e2e-attestations/claude-issue-951-0eease.json +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/e2e-attestations/claude-issue-962-0jjrfv.json +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/e2e-attestations/claude-issue-986-red-test-e7sjrb.json +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/e2e-attestations/claude-tackle-956-xzen74.json +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/migrations.d/2026-07-29-restore-python-310.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/migrations.d/2026-08-04-cli-in-process.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/migrations.d/2026-08-20-declared-table-name.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/migrations.d/README.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/src/lib.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/tests/__init__.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/tests/conftest.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/tests/e2e/__init__.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/__init__.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/dirsql.toml +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/tests/integration/__init__.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/tests/integration/binding/__init__.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/python/tests/integration/hermetic/__init__.py +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/CHANGELOG.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/MIGRATIONS.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/README.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/benches/db_bench.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/benches/differ_bench.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/benches/matcher_bench.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/benches/scanner_bench.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/explanation.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/getting-started.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/howto/embed.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/howto/persist.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/howto/query-json.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/index.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/plugins.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/reference/columns.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/reference/config.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/reference/hooks.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/reference/http-api.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/bin/dirsql.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/cli/execute.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/cli/init.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/cli/mod.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/cli/repl.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/cli/router.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/cli/run.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/cli/serialize.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/cli/server.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/cli/table.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/command.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/config.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/db.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/default_config.toml +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/differ.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/functions.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/infer.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/parsed_cache.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/parsed_vtab.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/path_table.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/persist.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/sql_literal.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/vtab.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/packages/rust/src/watcher.rs +0 -0
- {dirsql-0.4.30 → dirsql-0.4.32}/pyproject.toml +0 -0
|
@@ -540,7 +540,7 @@ checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
|
|
540
540
|
|
|
541
541
|
[[package]]
|
|
542
542
|
name = "dirsql"
|
|
543
|
-
version = "0.4.
|
|
543
|
+
version = "0.4.32"
|
|
544
544
|
dependencies = [
|
|
545
545
|
"assert_cmd",
|
|
546
546
|
"axum",
|
|
@@ -583,7 +583,7 @@ dependencies = [
|
|
|
583
583
|
|
|
584
584
|
[[package]]
|
|
585
585
|
name = "dirsql-py-ext"
|
|
586
|
-
version = "0.4.
|
|
586
|
+
version = "0.4.32"
|
|
587
587
|
dependencies = [
|
|
588
588
|
"dirsql",
|
|
589
589
|
"pyo3",
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
Ask a question in plain language and get the closest documents back — even
|
|
4
4
|
when they share no keywords with it. Install
|
|
5
5
|
[`dirsql-plugin-embeddings`](../plugins.md#dirsql-plugin-embeddings) and
|
|
6
|
-
semantic search becomes plain SQL: the plugin's `embed()` function turns
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
semantic search becomes plain SQL: the plugin's `embed()` function turns text
|
|
7
|
+
into vectors and loads [`sqlite-vec`](https://github.com/asg017/sqlite-vec),
|
|
8
|
+
which does the distance math. No API keys, no services — the model runs
|
|
9
|
+
locally.
|
|
10
10
|
|
|
11
11
|
Suppose short notes live in `notes/*.md`:
|
|
12
12
|
|
|
@@ -40,7 +40,32 @@ The first-ever run downloads the model (on the order of a hundred megabytes,
|
|
|
40
40
|
with progress on stderr); after that it loads from the local cache. Results
|
|
41
41
|
print one `path<TAB>distance` line per match, closest first.
|
|
42
42
|
|
|
43
|
-
##
|
|
43
|
+
## Which shape
|
|
44
|
+
|
|
45
|
+
The one-liner embeds every matched file on every run. That is the right trade
|
|
46
|
+
for a question you ask once, and the wrong one for a corpus you search
|
|
47
|
+
repeatedly — where a stored [`vec0` index](./search-indexes.md#vector-search-vec0)
|
|
48
|
+
embeds each file once, at ingest, and a query embeds only the question:
|
|
49
|
+
|
|
50
|
+
| | This page: path-table | [Vector index](./search-indexes.md#vector-search-vec0) |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| Setup | none | a `[[table]]` with a `ddl` batch |
|
|
53
|
+
| Freshness | always current — the walk is the read | watcher-maintained; survives restarts under [`--persist`](./persist.md) |
|
|
54
|
+
| Per query | one `embed()` round trip **per matched file**, plus a walk that reads every file's content | one `embed()` round trip **total**, plus an in-process KNN scan |
|
|
55
|
+
| Top-k | `ORDER BY … LIMIT k` | `MATCH … AND k = …` |
|
|
56
|
+
| Good for | a one-off question, a small corpus, an ad-hoc glob | a corpus you query repeatedly |
|
|
57
|
+
|
|
58
|
+
The index only pays off when the table outlives the query — under `--persist`,
|
|
59
|
+
or inside a long-running [`dirsql server`](../reference/cli.md). A one-shot
|
|
60
|
+
`dirsql query` against an ephemeral index rebuilds the table, and therefore
|
|
61
|
+
re-embeds the corpus, before it answers; that is strictly more work than the
|
|
62
|
+
subquery below. The full recipe — the width probe, the `ddl` batch, both
|
|
63
|
+
triggers, and what a model-id edit costs — is
|
|
64
|
+
[Add a search index to a table](./search-indexes.md#vector-search-vec0).
|
|
65
|
+
|
|
66
|
+
The rest of this page is the zero-setup shape.
|
|
67
|
+
|
|
68
|
+
## The SQL behind the one-liner
|
|
44
69
|
|
|
45
70
|
The one-liner generates and runs ordinary `dirsql` SQL, and you can write it
|
|
46
71
|
yourself when you want more than ranked paths — a different projection, a
|
|
@@ -79,7 +104,9 @@ Reading the query inside-out:
|
|
|
79
104
|
and its distance are NULL too — and SQLite sorts NULLs *first* ascending,
|
|
80
105
|
so without this line the unrankable files take the top-k slots.
|
|
81
106
|
4. `vec_distance_cosine(...)` computes cosine distance between the two
|
|
82
|
-
vectors; `ORDER BY distance LIMIT 3` keeps the three nearest.
|
|
107
|
+
vectors; `ORDER BY distance LIMIT 3` keeps the three nearest. There is no
|
|
108
|
+
`vec0` table here, so `sqlite-vec`'s `MATCH … AND k = …` does not apply —
|
|
109
|
+
for a plain expression, `ORDER BY … LIMIT k` *is* its documented top-k.
|
|
83
110
|
|
|
84
111
|
Structured files compose with SQL's JSON operators — embed one field instead
|
|
85
112
|
of the whole file:
|
|
@@ -93,24 +120,19 @@ ORDER BY vec_distance_cosine(emb, embed('local private models'))
|
|
|
93
120
|
LIMIT 10
|
|
94
121
|
```
|
|
95
122
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
above does not use: a `[[table]]`'s own `name` is always a per-file row table.
|
|
100
|
-
For plain expressions, `sqlite-vec`'s own documented pattern is exactly what
|
|
101
|
-
this guide uses: `ORDER BY vec_distance_cosine(...) LIMIT k`. To get the `vec0`
|
|
102
|
-
idiom instead, declare the `vec0` table alongside the row table in the same
|
|
103
|
-
[`ddl` batch](../reference/config.md#batch-ddl) and fill it from a trigger.
|
|
104
|
-
:::
|
|
123
|
+
The same projection works as an `on-file` hook feeding the indexed shape:
|
|
124
|
+
parse the field in the hook, store it as a column, and the trigger embeds it
|
|
125
|
+
once instead of on every query.
|
|
105
126
|
|
|
106
127
|
## Repeat runs are cheap
|
|
107
128
|
|
|
108
129
|
Computed vectors are cached on disk, keyed on content and model
|
|
109
130
|
([vector cache](../plugins.md#vector-cache)) — re-running a search over
|
|
110
|
-
unchanged files skips the model entirely and re-embeds only what changed.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
131
|
+
unchanged files skips the model entirely and re-embeds only what changed. That
|
|
132
|
+
takes the *inference* out of the shape above, but not the walk or the per-file
|
|
133
|
+
round trip; only a stored index removes those. And the plugin costs nothing
|
|
134
|
+
when idle: a query that never calls `embed()` spawns no worker and loads no
|
|
135
|
+
model ([zero cost when unused](../plugins.md#zero-cost-when-unused)).
|
|
114
136
|
|
|
115
137
|
## How `embed()` gets into SQL
|
|
116
138
|
|
|
@@ -155,8 +155,10 @@ ddl = '''
|
|
|
155
155
|
CREATE TABLE notes (slug TEXT, title TEXT, body TEXT);
|
|
156
156
|
|
|
157
157
|
-- Width must equal what the probe printed for the model id named below.
|
|
158
|
-
CREATE VIRTUAL TABLE notes_vec
|
|
159
|
-
|
|
158
|
+
CREATE VIRTUAL TABLE notes_vec
|
|
159
|
+
USING vec0(embedding float[512] distance_metric=cosine);
|
|
160
|
+
CREATE TRIGGER notes_vi AFTER INSERT ON notes
|
|
161
|
+
WHEN new.body IS NOT NULL BEGIN
|
|
160
162
|
INSERT INTO notes_vec(rowid, embedding)
|
|
161
163
|
VALUES (new.rowid, embed(new.body, 'minishlab/potion-retrieval-32M'));
|
|
162
164
|
END;
|
|
@@ -169,6 +171,29 @@ END;
|
|
|
169
171
|
The delete side is an ordinary `DELETE`, not FTS5's `'delete'` command row —
|
|
170
172
|
`vec0` is a normal-looking table that way.
|
|
171
173
|
|
|
174
|
+
Two clauses in there are load-bearing:
|
|
175
|
+
|
|
176
|
+
- **`distance_metric=cosine`.** `vec0` defaults to L2, which is not the metric
|
|
177
|
+
the rest of dirsql's semantic search uses — `vec_distance_cosine()` backs
|
|
178
|
+
both the plugin's one-liner and
|
|
179
|
+
[Search documents by meaning](./search-by-meaning.md). The two agree only for
|
|
180
|
+
vectors of equal length, so as soon as documents embed to vectors of
|
|
181
|
+
different magnitude they rank differently: against three notes, L2 returned
|
|
182
|
+
`a, c, b` where cosine returned `a, b, c`. Declaring the metric makes
|
|
183
|
+
`distance` the number `vec_distance_cosine()` would compute.
|
|
184
|
+
- **`WHEN new.body IS NOT NULL`.** `embed(NULL)` is `NULL`, and `vec0` rejects
|
|
185
|
+
a NULL vector outright — so without the guard a single row whose text is
|
|
186
|
+
missing fails the **whole** table load, not just its own insert:
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
dirsql query: failed to load config: SQLite error: Inserted vector for the
|
|
190
|
+
"embedding" column is invalid: Input must have type BLOB (compact format) or
|
|
191
|
+
TEXT (JSON), found NULL
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
With it, that row still lands in `notes`; only its vector is skipped. FTS5
|
|
195
|
+
needs no such guard — it indexes a NULL happily.
|
|
196
|
+
|
|
172
197
|
Query it with `sqlite-vec`'s KNN form, joined back on `rowid`:
|
|
173
198
|
|
|
174
199
|
```bash
|
|
@@ -460,3 +460,47 @@ Turn discovery off with either:
|
|
|
460
460
|
|
|
461
461
|
A plugin that declares itself but is missing its module or its `dirsql.toml`
|
|
462
462
|
fragment is a launcher error naming the package — never a silent skip.
|
|
463
|
+
|
|
464
|
+
## Progress reporting
|
|
465
|
+
|
|
466
|
+
Building the index over a large tree is not instant: the walk visits every
|
|
467
|
+
file, then each matched file costs one `on-file` round trip plus whatever the
|
|
468
|
+
table's `ddl` fires on insert. On a big corpus that is minutes. dirsql reports
|
|
469
|
+
the two phases on **stderr** while they run:
|
|
470
|
+
|
|
471
|
+
```
|
|
472
|
+
dirsql: scanning 128413 files
|
|
473
|
+
dirsql: indexing 9204/41231 files (22%)
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Each line is rewritten in place. When a phase ends its line is erased and
|
|
477
|
+
replaced by one summary of what it cost:
|
|
478
|
+
|
|
479
|
+
```
|
|
480
|
+
dirsql: scanned 128413 files in 4.2s
|
|
481
|
+
dirsql: indexed 41231 files in 3m12s
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
stdout is untouched — it carries the query result and nothing else.
|
|
485
|
+
|
|
486
|
+
By default this is **terminal-only, and only for work slow enough to wonder
|
|
487
|
+
about**: a phase that finishes in under half a second prints nothing at all,
|
|
488
|
+
and a run whose stderr is a pipe or a file prints nothing regardless of how
|
|
489
|
+
long it takes. `dirsql "…" 2>run.log` and `dirsql "…" | jq` are byte-for-byte
|
|
490
|
+
what they were before.
|
|
491
|
+
|
|
492
|
+
Override with `DIRSQL_PROGRESS`:
|
|
493
|
+
|
|
494
|
+
| Value | Effect |
|
|
495
|
+
|---|---|
|
|
496
|
+
| unset, or `auto` | Report only on a terminal, and only once a phase has run for half a second. The default. |
|
|
497
|
+
| `always`, `1`, `true` | Report from the first update, terminal or not. Use it to watch a scan whose stderr is redirected. |
|
|
498
|
+
| `never`, `0`, `false` | Report nothing, ever. |
|
|
499
|
+
|
|
500
|
+
Values are case-insensitive and surrounding whitespace is ignored; anything
|
|
501
|
+
unrecognized reads as `auto`, so a typo cannot stop a scan from running.
|
|
502
|
+
|
|
503
|
+
The setting is read by the **core**, not the CLI, so it governs an index built
|
|
504
|
+
from any SDK as well — a Python or TypeScript program that builds a `DirSQL`
|
|
505
|
+
with a terminal attached gets the same two phases on stderr, and the same
|
|
506
|
+
silence when piped.
|
|
@@ -396,3 +396,22 @@ not fit that range is a hard error, not a lossy conversion: Python raises
|
|
|
396
396
|
A TypeScript `bigint` **within** `i64` range maps to `INTEGER`. Only a real
|
|
397
397
|
`bytes`/`bytearray` (Python) or `Buffer`/`Uint8Array` (TypeScript) maps to
|
|
398
398
|
`BLOB` — a list/array of integers does not.
|
|
399
|
+
|
|
400
|
+
## Progress on construction
|
|
401
|
+
|
|
402
|
+
Constructing a `DirSQL` walks the tree and ingests every matched file, which
|
|
403
|
+
on a large corpus is the slowest thing your program does. The core reports
|
|
404
|
+
both phases on **stderr** while they run, then erases the live line and leaves
|
|
405
|
+
one summary of what each cost:
|
|
406
|
+
|
|
407
|
+
```
|
|
408
|
+
dirsql: indexed 41231 files in 3m12s
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
This is terminal-only by default, and only for a phase that runs longer than
|
|
412
|
+
half a second — a program whose stderr is a pipe, a file, or a log collector
|
|
413
|
+
sees nothing, whatever the scan costs. Set `DIRSQL_PROGRESS=never` to
|
|
414
|
+
guarantee silence even on a terminal, or `DIRSQL_PROGRESS=always` to report
|
|
415
|
+
regardless. The full table is in the
|
|
416
|
+
[CLI reference](cli.md#progress-reporting); the setting lives in the shared
|
|
417
|
+
core, so it behaves identically from all three SDKs.
|
|
@@ -4,7 +4,7 @@ name = "dirsql-py-ext"
|
|
|
4
4
|
# pypi/maturin handler can rewrite it via `write-version` before
|
|
5
5
|
# `maturin build`. `pyproject.toml` declares `dynamic = ["version"]`
|
|
6
6
|
# and maturin reads this field. Mirrors `packages/rust/Cargo.toml`.
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.32"
|
|
8
8
|
# abi3 note: the `abi3-py310` pyo3 feature (below) builds ONE stable-ABI
|
|
9
9
|
# `cp310-abi3` wheel per platform that loads on every CPython >= 3.10
|
|
10
10
|
# (matching `pyproject.toml`'s `requires-python = ">=3.10"`), instead of
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
Ask a question in plain language and get the closest documents back — even
|
|
4
4
|
when they share no keywords with it. Install
|
|
5
5
|
[`dirsql-plugin-embeddings`](../plugins.md#dirsql-plugin-embeddings) and
|
|
6
|
-
semantic search becomes plain SQL: the plugin's `embed()` function turns
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
semantic search becomes plain SQL: the plugin's `embed()` function turns text
|
|
7
|
+
into vectors and loads [`sqlite-vec`](https://github.com/asg017/sqlite-vec),
|
|
8
|
+
which does the distance math. No API keys, no services — the model runs
|
|
9
|
+
locally.
|
|
10
10
|
|
|
11
11
|
Suppose short notes live in `notes/*.md`:
|
|
12
12
|
|
|
@@ -40,7 +40,32 @@ The first-ever run downloads the model (on the order of a hundred megabytes,
|
|
|
40
40
|
with progress on stderr); after that it loads from the local cache. Results
|
|
41
41
|
print one `path<TAB>distance` line per match, closest first.
|
|
42
42
|
|
|
43
|
-
##
|
|
43
|
+
## Which shape
|
|
44
|
+
|
|
45
|
+
The one-liner embeds every matched file on every run. That is the right trade
|
|
46
|
+
for a question you ask once, and the wrong one for a corpus you search
|
|
47
|
+
repeatedly — where a stored [`vec0` index](./search-indexes.md#vector-search-vec0)
|
|
48
|
+
embeds each file once, at ingest, and a query embeds only the question:
|
|
49
|
+
|
|
50
|
+
| | This page: path-table | [Vector index](./search-indexes.md#vector-search-vec0) |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| Setup | none | a `[[table]]` with a `ddl` batch |
|
|
53
|
+
| Freshness | always current — the walk is the read | watcher-maintained; survives restarts under [`--persist`](./persist.md) |
|
|
54
|
+
| Per query | one `embed()` round trip **per matched file**, plus a walk that reads every file's content | one `embed()` round trip **total**, plus an in-process KNN scan |
|
|
55
|
+
| Top-k | `ORDER BY … LIMIT k` | `MATCH … AND k = …` |
|
|
56
|
+
| Good for | a one-off question, a small corpus, an ad-hoc glob | a corpus you query repeatedly |
|
|
57
|
+
|
|
58
|
+
The index only pays off when the table outlives the query — under `--persist`,
|
|
59
|
+
or inside a long-running [`dirsql server`](../reference/cli.md). A one-shot
|
|
60
|
+
`dirsql query` against an ephemeral index rebuilds the table, and therefore
|
|
61
|
+
re-embeds the corpus, before it answers; that is strictly more work than the
|
|
62
|
+
subquery below. The full recipe — the width probe, the `ddl` batch, both
|
|
63
|
+
triggers, and what a model-id edit costs — is
|
|
64
|
+
[Add a search index to a table](./search-indexes.md#vector-search-vec0).
|
|
65
|
+
|
|
66
|
+
The rest of this page is the zero-setup shape.
|
|
67
|
+
|
|
68
|
+
## The SQL behind the one-liner
|
|
44
69
|
|
|
45
70
|
The one-liner generates and runs ordinary `dirsql` SQL, and you can write it
|
|
46
71
|
yourself when you want more than ranked paths — a different projection, a
|
|
@@ -79,7 +104,9 @@ Reading the query inside-out:
|
|
|
79
104
|
and its distance are NULL too — and SQLite sorts NULLs *first* ascending,
|
|
80
105
|
so without this line the unrankable files take the top-k slots.
|
|
81
106
|
4. `vec_distance_cosine(...)` computes cosine distance between the two
|
|
82
|
-
vectors; `ORDER BY distance LIMIT 3` keeps the three nearest.
|
|
107
|
+
vectors; `ORDER BY distance LIMIT 3` keeps the three nearest. There is no
|
|
108
|
+
`vec0` table here, so `sqlite-vec`'s `MATCH … AND k = …` does not apply —
|
|
109
|
+
for a plain expression, `ORDER BY … LIMIT k` *is* its documented top-k.
|
|
83
110
|
|
|
84
111
|
Structured files compose with SQL's JSON operators — embed one field instead
|
|
85
112
|
of the whole file:
|
|
@@ -93,24 +120,19 @@ ORDER BY vec_distance_cosine(emb, embed('local private models'))
|
|
|
93
120
|
LIMIT 10
|
|
94
121
|
```
|
|
95
122
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
above does not use: a `[[table]]`'s own `name` is always a per-file row table.
|
|
100
|
-
For plain expressions, `sqlite-vec`'s own documented pattern is exactly what
|
|
101
|
-
this guide uses: `ORDER BY vec_distance_cosine(...) LIMIT k`. To get the `vec0`
|
|
102
|
-
idiom instead, declare the `vec0` table alongside the row table in the same
|
|
103
|
-
[`ddl` batch](../reference/config.md#batch-ddl) and fill it from a trigger.
|
|
104
|
-
:::
|
|
123
|
+
The same projection works as an `on-file` hook feeding the indexed shape:
|
|
124
|
+
parse the field in the hook, store it as a column, and the trigger embeds it
|
|
125
|
+
once instead of on every query.
|
|
105
126
|
|
|
106
127
|
## Repeat runs are cheap
|
|
107
128
|
|
|
108
129
|
Computed vectors are cached on disk, keyed on content and model
|
|
109
130
|
([vector cache](../plugins.md#vector-cache)) — re-running a search over
|
|
110
|
-
unchanged files skips the model entirely and re-embeds only what changed.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
131
|
+
unchanged files skips the model entirely and re-embeds only what changed. That
|
|
132
|
+
takes the *inference* out of the shape above, but not the walk or the per-file
|
|
133
|
+
round trip; only a stored index removes those. And the plugin costs nothing
|
|
134
|
+
when idle: a query that never calls `embed()` spawns no worker and loads no
|
|
135
|
+
model ([zero cost when unused](../plugins.md#zero-cost-when-unused)).
|
|
114
136
|
|
|
115
137
|
## How `embed()` gets into SQL
|
|
116
138
|
|
|
@@ -155,8 +155,10 @@ ddl = '''
|
|
|
155
155
|
CREATE TABLE notes (slug TEXT, title TEXT, body TEXT);
|
|
156
156
|
|
|
157
157
|
-- Width must equal what the probe printed for the model id named below.
|
|
158
|
-
CREATE VIRTUAL TABLE notes_vec
|
|
159
|
-
|
|
158
|
+
CREATE VIRTUAL TABLE notes_vec
|
|
159
|
+
USING vec0(embedding float[512] distance_metric=cosine);
|
|
160
|
+
CREATE TRIGGER notes_vi AFTER INSERT ON notes
|
|
161
|
+
WHEN new.body IS NOT NULL BEGIN
|
|
160
162
|
INSERT INTO notes_vec(rowid, embedding)
|
|
161
163
|
VALUES (new.rowid, embed(new.body, 'minishlab/potion-retrieval-32M'));
|
|
162
164
|
END;
|
|
@@ -169,6 +171,29 @@ END;
|
|
|
169
171
|
The delete side is an ordinary `DELETE`, not FTS5's `'delete'` command row —
|
|
170
172
|
`vec0` is a normal-looking table that way.
|
|
171
173
|
|
|
174
|
+
Two clauses in there are load-bearing:
|
|
175
|
+
|
|
176
|
+
- **`distance_metric=cosine`.** `vec0` defaults to L2, which is not the metric
|
|
177
|
+
the rest of dirsql's semantic search uses — `vec_distance_cosine()` backs
|
|
178
|
+
both the plugin's one-liner and
|
|
179
|
+
[Search documents by meaning](./search-by-meaning.md). The two agree only for
|
|
180
|
+
vectors of equal length, so as soon as documents embed to vectors of
|
|
181
|
+
different magnitude they rank differently: against three notes, L2 returned
|
|
182
|
+
`a, c, b` where cosine returned `a, b, c`. Declaring the metric makes
|
|
183
|
+
`distance` the number `vec_distance_cosine()` would compute.
|
|
184
|
+
- **`WHEN new.body IS NOT NULL`.** `embed(NULL)` is `NULL`, and `vec0` rejects
|
|
185
|
+
a NULL vector outright — so without the guard a single row whose text is
|
|
186
|
+
missing fails the **whole** table load, not just its own insert:
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
dirsql query: failed to load config: SQLite error: Inserted vector for the
|
|
190
|
+
"embedding" column is invalid: Input must have type BLOB (compact format) or
|
|
191
|
+
TEXT (JSON), found NULL
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
With it, that row still lands in `notes`; only its vector is skipped. FTS5
|
|
195
|
+
needs no such guard — it indexes a NULL happily.
|
|
196
|
+
|
|
172
197
|
Query it with `sqlite-vec`'s KNN form, joined back on `rowid`:
|
|
173
198
|
|
|
174
199
|
```bash
|
|
@@ -460,3 +460,47 @@ Turn discovery off with either:
|
|
|
460
460
|
|
|
461
461
|
A plugin that declares itself but is missing its module or its `dirsql.toml`
|
|
462
462
|
fragment is a launcher error naming the package — never a silent skip.
|
|
463
|
+
|
|
464
|
+
## Progress reporting
|
|
465
|
+
|
|
466
|
+
Building the index over a large tree is not instant: the walk visits every
|
|
467
|
+
file, then each matched file costs one `on-file` round trip plus whatever the
|
|
468
|
+
table's `ddl` fires on insert. On a big corpus that is minutes. dirsql reports
|
|
469
|
+
the two phases on **stderr** while they run:
|
|
470
|
+
|
|
471
|
+
```
|
|
472
|
+
dirsql: scanning 128413 files
|
|
473
|
+
dirsql: indexing 9204/41231 files (22%)
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Each line is rewritten in place. When a phase ends its line is erased and
|
|
477
|
+
replaced by one summary of what it cost:
|
|
478
|
+
|
|
479
|
+
```
|
|
480
|
+
dirsql: scanned 128413 files in 4.2s
|
|
481
|
+
dirsql: indexed 41231 files in 3m12s
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
stdout is untouched — it carries the query result and nothing else.
|
|
485
|
+
|
|
486
|
+
By default this is **terminal-only, and only for work slow enough to wonder
|
|
487
|
+
about**: a phase that finishes in under half a second prints nothing at all,
|
|
488
|
+
and a run whose stderr is a pipe or a file prints nothing regardless of how
|
|
489
|
+
long it takes. `dirsql "…" 2>run.log` and `dirsql "…" | jq` are byte-for-byte
|
|
490
|
+
what they were before.
|
|
491
|
+
|
|
492
|
+
Override with `DIRSQL_PROGRESS`:
|
|
493
|
+
|
|
494
|
+
| Value | Effect |
|
|
495
|
+
|---|---|
|
|
496
|
+
| unset, or `auto` | Report only on a terminal, and only once a phase has run for half a second. The default. |
|
|
497
|
+
| `always`, `1`, `true` | Report from the first update, terminal or not. Use it to watch a scan whose stderr is redirected. |
|
|
498
|
+
| `never`, `0`, `false` | Report nothing, ever. |
|
|
499
|
+
|
|
500
|
+
Values are case-insensitive and surrounding whitespace is ignored; anything
|
|
501
|
+
unrecognized reads as `auto`, so a typo cannot stop a scan from running.
|
|
502
|
+
|
|
503
|
+
The setting is read by the **core**, not the CLI, so it governs an index built
|
|
504
|
+
from any SDK as well — a Python or TypeScript program that builds a `DirSQL`
|
|
505
|
+
with a terminal attached gets the same two phases on stderr, and the same
|
|
506
|
+
silence when piped.
|
|
@@ -396,3 +396,22 @@ not fit that range is a hard error, not a lossy conversion: Python raises
|
|
|
396
396
|
A TypeScript `bigint` **within** `i64` range maps to `INTEGER`. Only a real
|
|
397
397
|
`bytes`/`bytearray` (Python) or `Buffer`/`Uint8Array` (TypeScript) maps to
|
|
398
398
|
`BLOB` — a list/array of integers does not.
|
|
399
|
+
|
|
400
|
+
## Progress on construction
|
|
401
|
+
|
|
402
|
+
Constructing a `DirSQL` walks the tree and ingests every matched file, which
|
|
403
|
+
on a large corpus is the slowest thing your program does. The core reports
|
|
404
|
+
both phases on **stderr** while they run, then erases the live line and leaves
|
|
405
|
+
one summary of what each cost:
|
|
406
|
+
|
|
407
|
+
```
|
|
408
|
+
dirsql: indexed 41231 files in 3m12s
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
This is terminal-only by default, and only for a phase that runs longer than
|
|
412
|
+
half a second — a program whose stderr is a pipe, a file, or a log collector
|
|
413
|
+
sees nothing, whatever the scan costs. Set `DIRSQL_PROGRESS=never` to
|
|
414
|
+
guarantee silence even on a terminal, or `DIRSQL_PROGRESS=always` to report
|
|
415
|
+
regardless. The full table is in the
|
|
416
|
+
[CLI reference](cli.md#progress-reporting); the setting lives in the shared
|
|
417
|
+
core, so it behaves identically from all three SDKs.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Per-package config so `[e2e]` can be per-package: it is one global table
|
|
2
|
+
# applied to every caller reading a given config, so "the core plus MY binding"
|
|
3
|
+
# has nowhere else to live. Folded back into the root file, a napi change would
|
|
4
|
+
# start staling this package.
|
|
5
|
+
#
|
|
6
|
+
# The PyO3 glue is in scope because the console script runs the CLI in-process
|
|
7
|
+
# through it (#721), so every e2e case crosses it. packages/ts/napi is not: it
|
|
8
|
+
# cannot reach this suite.
|
|
9
|
+
[e2e]
|
|
10
|
+
extra_scope = ["packages/rust/src", "packages/python/src"]
|
|
11
|
+
|
|
12
|
+
[python]
|
|
13
|
+
one_function_per_file = { max_lines = 100 }
|
|
14
|
+
|
|
15
|
+
# Same floor as the root file, which this lane no longer reads.
|
|
16
|
+
[python.coverage]
|
|
17
|
+
fail_under = 100
|
|
18
|
+
branch = true
|
|
19
|
+
|
|
20
|
+
# The hermetic integration tier is defined by patching the first-party core
|
|
21
|
+
# boundary: `dirsql._async._RustDirSQL` is the PyO3 class, and replacing it is
|
|
22
|
+
# what lets these tests run SQLite-free and native-build-free. There is no
|
|
23
|
+
# third-party target to re-root onto, so `no-first-party-patch` has nothing to
|
|
24
|
+
# say about this seam. Every other patch in both files targets stdlib and stays
|
|
25
|
+
# gated.
|
|
26
|
+
[[python.exempt]]
|
|
27
|
+
path = "tests/integration/hermetic/dirsql_test.py"
|
|
28
|
+
rules = ["no-first-party-patch"]
|
|
29
|
+
reason = "the hermetic tier IS the _RustDirSQL core-boundary patch; no third-party target exists to move it to"
|
|
30
|
+
|
|
31
|
+
[[python.exempt]]
|
|
32
|
+
path = "tests/integration/hermetic/extensions_test.py"
|
|
33
|
+
rules = ["no-first-party-patch"]
|
|
34
|
+
reason = "the hermetic tier IS the _RustDirSQL core-boundary patch; no third-party target exists to move it to"
|
|
@@ -6,7 +6,7 @@ description = "Ephemeral SQL index over a local directory"
|
|
|
6
6
|
# version stays for the binding crates (publish = false). The literal
|
|
7
7
|
# intentionally lags the published version: putitoutthere rewrites it to
|
|
8
8
|
# the planned release version at build time and never commits it back.
|
|
9
|
-
version = "0.4.
|
|
9
|
+
version = "0.4.32"
|
|
10
10
|
edition.workspace = true
|
|
11
11
|
# Literal `license` (not `license.workspace = true`) because
|
|
12
12
|
# putitoutthere's preflight check reads `[package].license` directly
|
{dirsql-0.4.30/packages/python → dirsql-0.4.32/packages/rust}/docs/howto/search-by-meaning.md
RENAMED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
Ask a question in plain language and get the closest documents back — even
|
|
4
4
|
when they share no keywords with it. Install
|
|
5
5
|
[`dirsql-plugin-embeddings`](../plugins.md#dirsql-plugin-embeddings) and
|
|
6
|
-
semantic search becomes plain SQL: the plugin's `embed()` function turns
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
semantic search becomes plain SQL: the plugin's `embed()` function turns text
|
|
7
|
+
into vectors and loads [`sqlite-vec`](https://github.com/asg017/sqlite-vec),
|
|
8
|
+
which does the distance math. No API keys, no services — the model runs
|
|
9
|
+
locally.
|
|
10
10
|
|
|
11
11
|
Suppose short notes live in `notes/*.md`:
|
|
12
12
|
|
|
@@ -40,7 +40,32 @@ The first-ever run downloads the model (on the order of a hundred megabytes,
|
|
|
40
40
|
with progress on stderr); after that it loads from the local cache. Results
|
|
41
41
|
print one `path<TAB>distance` line per match, closest first.
|
|
42
42
|
|
|
43
|
-
##
|
|
43
|
+
## Which shape
|
|
44
|
+
|
|
45
|
+
The one-liner embeds every matched file on every run. That is the right trade
|
|
46
|
+
for a question you ask once, and the wrong one for a corpus you search
|
|
47
|
+
repeatedly — where a stored [`vec0` index](./search-indexes.md#vector-search-vec0)
|
|
48
|
+
embeds each file once, at ingest, and a query embeds only the question:
|
|
49
|
+
|
|
50
|
+
| | This page: path-table | [Vector index](./search-indexes.md#vector-search-vec0) |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| Setup | none | a `[[table]]` with a `ddl` batch |
|
|
53
|
+
| Freshness | always current — the walk is the read | watcher-maintained; survives restarts under [`--persist`](./persist.md) |
|
|
54
|
+
| Per query | one `embed()` round trip **per matched file**, plus a walk that reads every file's content | one `embed()` round trip **total**, plus an in-process KNN scan |
|
|
55
|
+
| Top-k | `ORDER BY … LIMIT k` | `MATCH … AND k = …` |
|
|
56
|
+
| Good for | a one-off question, a small corpus, an ad-hoc glob | a corpus you query repeatedly |
|
|
57
|
+
|
|
58
|
+
The index only pays off when the table outlives the query — under `--persist`,
|
|
59
|
+
or inside a long-running [`dirsql server`](../reference/cli.md). A one-shot
|
|
60
|
+
`dirsql query` against an ephemeral index rebuilds the table, and therefore
|
|
61
|
+
re-embeds the corpus, before it answers; that is strictly more work than the
|
|
62
|
+
subquery below. The full recipe — the width probe, the `ddl` batch, both
|
|
63
|
+
triggers, and what a model-id edit costs — is
|
|
64
|
+
[Add a search index to a table](./search-indexes.md#vector-search-vec0).
|
|
65
|
+
|
|
66
|
+
The rest of this page is the zero-setup shape.
|
|
67
|
+
|
|
68
|
+
## The SQL behind the one-liner
|
|
44
69
|
|
|
45
70
|
The one-liner generates and runs ordinary `dirsql` SQL, and you can write it
|
|
46
71
|
yourself when you want more than ranked paths — a different projection, a
|
|
@@ -79,7 +104,9 @@ Reading the query inside-out:
|
|
|
79
104
|
and its distance are NULL too — and SQLite sorts NULLs *first* ascending,
|
|
80
105
|
so without this line the unrankable files take the top-k slots.
|
|
81
106
|
4. `vec_distance_cosine(...)` computes cosine distance between the two
|
|
82
|
-
vectors; `ORDER BY distance LIMIT 3` keeps the three nearest.
|
|
107
|
+
vectors; `ORDER BY distance LIMIT 3` keeps the three nearest. There is no
|
|
108
|
+
`vec0` table here, so `sqlite-vec`'s `MATCH … AND k = …` does not apply —
|
|
109
|
+
for a plain expression, `ORDER BY … LIMIT k` *is* its documented top-k.
|
|
83
110
|
|
|
84
111
|
Structured files compose with SQL's JSON operators — embed one field instead
|
|
85
112
|
of the whole file:
|
|
@@ -93,24 +120,19 @@ ORDER BY vec_distance_cosine(emb, embed('local private models'))
|
|
|
93
120
|
LIMIT 10
|
|
94
121
|
```
|
|
95
122
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
above does not use: a `[[table]]`'s own `name` is always a per-file row table.
|
|
100
|
-
For plain expressions, `sqlite-vec`'s own documented pattern is exactly what
|
|
101
|
-
this guide uses: `ORDER BY vec_distance_cosine(...) LIMIT k`. To get the `vec0`
|
|
102
|
-
idiom instead, declare the `vec0` table alongside the row table in the same
|
|
103
|
-
[`ddl` batch](../reference/config.md#batch-ddl) and fill it from a trigger.
|
|
104
|
-
:::
|
|
123
|
+
The same projection works as an `on-file` hook feeding the indexed shape:
|
|
124
|
+
parse the field in the hook, store it as a column, and the trigger embeds it
|
|
125
|
+
once instead of on every query.
|
|
105
126
|
|
|
106
127
|
## Repeat runs are cheap
|
|
107
128
|
|
|
108
129
|
Computed vectors are cached on disk, keyed on content and model
|
|
109
130
|
([vector cache](../plugins.md#vector-cache)) — re-running a search over
|
|
110
|
-
unchanged files skips the model entirely and re-embeds only what changed.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
131
|
+
unchanged files skips the model entirely and re-embeds only what changed. That
|
|
132
|
+
takes the *inference* out of the shape above, but not the walk or the per-file
|
|
133
|
+
round trip; only a stored index removes those. And the plugin costs nothing
|
|
134
|
+
when idle: a query that never calls `embed()` spawns no worker and loads no
|
|
135
|
+
model ([zero cost when unused](../plugins.md#zero-cost-when-unused)).
|
|
114
136
|
|
|
115
137
|
## How `embed()` gets into SQL
|
|
116
138
|
|