From 47f3a703bf06e17309e00a3696105e43775a81ed Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 9 Nov 2026 15:42:56 -0500 Subject: [PATCH] perf(search): prefix-by-default Deployments and config-audit search; bounded Contains opt-in (ScadaBridge#50) Deployments free-text search and the config-audit EntityName filter no longer emit LIKE '%term%' by default. Prefix (LIKE 'term%') is the default; substring is an explicit opt-in (DeploymentSearchMode.Contains / entityNameContains) always bounded by a DeployedAt/Timestamp window (explicit, else 30 days). Migration PrefixSearchIndexes adds DeployedBy, RevisionHash and EntityName indexes. Deployments page gains a 'Match anywhere' checkbox. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV --- CLAUDE.md | 1 + docs/plans/sql/PrefixSearchIndexes.sql | 26 + .../Pages/Deployment/Deployments.razor | 26 +- .../Repositories/ICentralUiRepository.cs | 12 +- .../Types/Deployment/DeploymentListFilter.cs | 41 +- .../Configurations/AuditConfiguration.cs | 2 + .../Configurations/DeploymentConfiguration.cs | 5 + ...1109203753_PrefixSearchIndexes.Designer.cs | 2184 +++++++++++++++++ .../20261109203753_PrefixSearchIndexes.cs | 45 + .../ScadaBridgeDbContextModelSnapshot.cs | 9 + .../Repositories/CentralUiRepository.cs | 76 +- .../DeploymentManagerRepository.cs | 30 +- ...W.ScadaBridge.ConfigurationDatabase.csproj | 4 + .../DeploymentListQueryRepositoryTests.cs | 2 +- .../SearchPrefixQueryTests.cs | 201 ++ 15 files changed, 2620 insertions(+), 44 deletions(-) create mode 100644 docs/plans/sql/PrefixSearchIndexes.sql create mode 100644 src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Migrations/20261109203753_PrefixSearchIndexes.Designer.cs create mode 100644 src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Migrations/20261109203753_PrefixSearchIndexes.cs create mode 100644 tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/SearchPrefixQueryTests.cs diff --git a/CLAUDE.md b/CLAUDE.md index 21a8476f..0f345c03 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -229,6 +229,7 @@ spec for each is `docs/requirements/Component-.md`, and `README.md` carrie - Host readiness gating: `/health/ready` endpoint, no traffic until operational. - EF Core migrations: auto-apply in dev, manual SQL scripts for production. Gotcha: `dotnet ef migrations add … --no-build` scaffolds off the previously-built DLL and can emit an **empty** migration — build first (drop the `--no-build`), and delete the empty `Migrations/` files if it happens (`migrations remove` needs a live DB). - Audit logging absorbed into Configuration Database component (IAuditService). The config `AuditLogEntries` table stores **summaries** (a script body is recorded as length + SHA-256), caps `AfterStateJson` at 256 K chars, pages deterministically on `(Timestamp DESC, Id DESC)`, and has an optional purge: `ScadaBridge:ConfigAudit:RetentionDays` (default `0` = retained indefinitely), `PurgeBatchSize` 5000, `MaxBatchesPerTick` 200, `PurgeInterval` (bound with `ValidateOnStart` on Central). +- **Search is prefix by default (ScadaBridge#50, register row 36).** The Deployments page search (instance name, deployment id, revision hash, user) and the config-audit `EntityName` filter are `LIKE 'term%'` (index-seekable); a substring match is an explicit opt-in (`DeploymentListFilter.SearchMode = Contains` / the page's "Match anywhere" checkbox; `GetAuditLogEntriesAsync(entityNameContains: true)`) that is ALWAYS bounded to a `DeployedAt`/`Timestamp` window (explicit `DeployedFrom`/`from`, else the last 30 days). Migration `PrefixSearchIndexes` (script `docs/plans/sql/PrefixSearchIndexes.sql`; not applied to QA/prod) adds the `DeployedBy`, `RevisionHash` and `AuditLogEntries.EntityName` indexes. Tests: `SearchPrefixQueryTests` (SQL-shape via `ToQueryString`). - **Review remediation 2026-10-21** (8-slice system review, ~107 findings, 4 High; record `docs/plans/2026-10-21-system-review-remediation.md`). Decisions: CLI uses bearer-per-invocation (see Security); the never-read `Database:MachineDataDb` option was **removed** (it was a required boot key); config audit stores summaries + retention (above); a **bound Test Run** (Test Run against a deployed instance) now needs Deployer + site scope and writes an audit row; persisted static overrides **seed** script state at actor start without firing scripts (alarms still evaluate). Also changed: site deploy gate rejects non-actor-safe (`ActorNameRules`) instance/script/alarm/native-source names and Data Connection actor names are ASCII-sanitised with collision rejection; site gRPC addresses are validated on every write path (management + Transport import); artifact applies are serialised per site with a 10-min stuck-apply watchdog; `IDataConnection.WriteBatchAndWaitAsync` is **gone** (the script-level handshake composes `WriteTagBatchRequest` + waiter); `instance list --search` is a **prefix** match paged in SQL; command-plane failback probes readiness with a real RPC (probation 5 min); `Transport:SchemaVersionMajor` removed; migration `ReviewRemediationIndexes` (script `docs/plans/sql/ReviewRemediationIndexes.sql`; **QA/prod not yet applied**). New register rows 128-135; row 117 closed. ### Akka.NET Conventions diff --git a/docs/plans/sql/PrefixSearchIndexes.sql b/docs/plans/sql/PrefixSearchIndexes.sql new file mode 100644 index 00000000..ebd06890 --- /dev/null +++ b/docs/plans/sql/PrefixSearchIndexes.sql @@ -0,0 +1,26 @@ +-- PrefixSearchIndexes (2026-11-09, ScadaBridge#50). Indexes backing the prefix search on the +-- Deployments page (DeployedBy, RevisionHash) and the config-audit EntityName filter. +BEGIN TRANSACTION; +ALTER TABLE [TemplateNativeAlarmSources] ADD [IsMesReportable] bit NOT NULL DEFAULT CAST(0 AS bit); + +ALTER TABLE [InstanceNativeAlarmSourceOverrides] ADD [IsMesReportableOverride] bit NULL; + +INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion]) +VALUES (N'20261109200211_AddNativeAlarmSourceMesReportable', N'10.0.13'); + +COMMIT; +GO + +BEGIN TRANSACTION; +CREATE INDEX [IX_DeploymentRecords_DeployedBy] ON [DeploymentRecords] ([DeployedBy]); + +CREATE INDEX [IX_DeploymentRecords_RevisionHash] ON [DeploymentRecords] ([RevisionHash]); + +CREATE INDEX [IX_AuditLogEntries_EntityName] ON [AuditLogEntries] ([EntityName]); + +INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion]) +VALUES (N'20261109203753_PrefixSearchIndexes', N'10.0.13'); + +COMMIT; +GO + diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/Deployments.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/Deployments.razor index c2a7a40c..4dca5892 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/Deployments.razor +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/Deployments.razor @@ -75,6 +75,15 @@ data-test="deploy-search" value="@_searchInput" @oninput="OnSearchInput" /> +
+ + +