Skip to content

aimdb-persistence: migrate example keys and patterns to the MQTT grammar and cover it in tests #214

Description

@lxsaah

Context

#201 changed PersistenceBackend::query from a prefix-glob contract to the
MQTT-style grammar subscriptions already use: * covers exactly one
dot-separated segment, # covers zero or more. The implementation and the
CHANGELOGs landed with the PR; the documentation and tests that teach the old
grammar did not.

Raised in review as
thread 23,
where it was agreed as non-blocking for that PR but required before release
the affected files are published crate documentation for a grammar that now
fails silently. This issue is what that thread is waiting on.

Line references are as of b790143.

The problem is the record keys, not just the patterns

The obvious read is "replace ::* with .#". That is not sufficient, and doing
only that would leave the examples incoherent.

Every persistence example keys its records with :: as the separator —
accuracy::vienna, my_record::key, sensor::a. A wildcard must be its own
whole dot-separated segment, so accuracy::vienna is a single segment and no
pattern can partition that key space at all. accuracy::* is likewise one
segment containing a literal *, so it is a literal key that matches nothing:

// aimdb-persistence/src/pattern.rs:66
fn a_wildcard_must_be_its_own_segment() {
    assert_eq!(literal_prefix("sensors*"), "sensors*");

aimdb-persistence-sqlite/CHANGELOG.md:20 already records the consequence
("Patterns over keys that are not dot-separated ("temp::*") no longer
wildcard") — but every example still uses exactly such keys.

So the sweep is: migrate the example keys to dot-separated, then fix the
patterns over them.
A reader who copies today's README gets a query that
returns zero rows and no error.

The sections below are commit-sized. §1–§3 are pure documentation; §4 is the
test change and is the only one that can fail CI.

1. aimdb-persistence/README.md

Keys:

  • :17.persist("my_record::key") in the API table.
  • :67-71configure::<Accuracy>("accuracy::vienna") / ("accuracy::berlin")
    and their matching reg.persist(...) calls.
  • :137-138configure::<MyRecord>("my_record::key") + reg.persist(...).
  • :140 — the commented-out format!("my_record::{}", city).

Patterns over them:

  • :77query_latest("accuracy::*", 10), captioned "Latest 10 per city".
  • :153, :162, :167, :182"my_record::*" across query_latest,
    query_range, query_raw and the error-handling example.
  • :152 — the prose "pattern supports * wildcard", which now needs to name
    both wildcards and say they are whole segments.

With keys migrated to accuracy.vienna / accuracy.berlin, the right pattern at
:77 is accuracy.*, not accuracy.# — both keys are exactly one segment
below accuracy, and * says so. Reserve # for the cases where depth varies;
query_raw at :167 is the one that feeds the AimX record.query handler, so
# is the honest choice there (see remote::QUERY_ALL_PATTERN). Worth one line
of prose contrasting the two, since choosing between them is the thing the old
grammar never made anyone think about.

2. aimdb-persistence-sqlite/README.md

Same treatment, smaller surface: :54-55 (configure + persist on
accuracy::vienna) and :60 (query_latest("accuracy::*", 5)).

Do not touch CHANGELOG.md:20 — its "temp::*" is a historical entry
describing the break, and is correct as written.

3. Rustdoc

  • aimdb-persistence/src/query_ext.rs:19-21query_latest's "Pattern
    support: "accuracy::*" returns latest N from each matching record. Single
    record: "accuracy::vienna"". Both halves are wrong now: the first matches
    nothing, and the second is no longer contrastive since it is just a
    wildcard-free pattern. query_range and query_raw below it say "pattern"
    without defining it and can point at the canonical statement instead of
    restating it.
  • aimdb-persistence/src/backend.rs:19StoredValue::record_name's example
    value is "accuracy::vienna". This is the type the backend contract is
    written against, so the stale key sits directly beside the corrected grammar.

backend.rs:52-61 (PersistenceBackend::query) and pattern.rs are already
correct and are the wording the rest should defer to. Nothing to change there.

4. aimdb-persistence/tests/query_skip_invalid.rs

Keys at :100, :105, :110, :115, :163, :168, :173, :199, :204,
:258, :279, :284, :289; patterns at :133, :182, :213, :237,
:266, :298; the doc table at :91-96.

These tests pass today and would keep passing after a careless key rename,
which is the actual gap the review named:

// :50 — Returns all pre-configured rows unconditionally (params ignored).
fn query<'a>(&'a self, _record_pattern: &'a str, _params: QueryParams)

MockBackend::query ignores the pattern entirely, so no test in the crate proves
a pattern ever reaches a backend, let alone that it is interpreted under the new
grammar. The six tests here exercise the filter_map in AimDbQueryExt and are
right to keep that focus — but the mock should honor record_pattern via the
re-exported topic_matches so the suite stops being blind to the grammar, and
one case should pin the behavior that motivated the change: a sensor.* query
must not return sensor.deep.nested.

aimdb-persistence-sqlite has real coverage of this against SQLite
(src/lib.rs:266 filters rows through topic_matches); what is missing is
coverage at the backend-contract level, which is where a third-party backend
author would look.

5. Out of scope — but confirm before closing

:: keys also appear in examples/remote-access-demo/src/server.rs:136-181 and
aimdb-wasm-adapter/tests/transform_join_integration_tests.rs:42-48. Neither is
broken: the demo never persists and subscribes to one exact key
(client.rs:179), and exact-match lookup is unaffected by the grammar. They are
only unwildcardable, which nothing there tries to do.

Leaving them is defensible. Changing them is also defensible, as the ::
convention is what led the persistence docs into this in the first place. Make it
an explicit decision rather than an oversight, and if they stay, they should not
be cited anywhere as examples of a pattern-friendly key.

Acceptance criteria

  1. grep -rn '::\*' aimdb-persistence/ aimdb-persistence-sqlite/ returns only
    CHANGELOG.md entries.
  2. No persistence example, rustdoc or test registers a record key that a
    wildcard cannot address — i.e. no :: in any key that an accompanying
    pattern is meant to match.
  3. MockBackend::query in query_skip_invalid.rs honors record_pattern
    through topic_matches, and a regression asserts sensor.* excludes
    sensor.deep.nested.
  4. Prose that mentions patterns names both * (exactly one segment) and #
    (zero or more) and states that a wildcard is a whole segment; the
    crate-level statement stays in backend.rs with others referring to it
    rather than restating it.
  5. make check green.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions