feat(query): PDF Package-2 associative log lookup via flags/metatags
Implements the source PDF's 'put(C,Z,Y,X,bytes,flags)' + 'scan_by_flag'
associative-storage API so records are discoverable by WHAT they are
(event type / doc_type metatag) rather than WHERE they live (coordinate
path) — the 'log lookup by query' the OS layers want.
cubestore:
- CubeStore::scan_by_flag(u16) / scan_by_type(&str): predicate scans
over decoded CubeHeader (not prefix scans).
- CubeStore::query_by_flag / query_by_type: same, but also return the
decoded (label, header, body) so a query tool can present results.
- get_record's raw/un-enveloped fallback now refresh_flags() so
synthesized headers carry SIZE_BYTES and are visible to flag queries.
- regression tests: scan_by_flag_finds_typed_records,
scan_by_type_event_lookup.
cubesys (daemon):
- new 'put' verb: enveloped write with optional doc_type=/title=
metatags (the PDF's put API). rawput remains for bulk/append payloads.
- ConcurrentStore::query_doc_type was already the backing predicate the
'query <doc_type>' verb uses; it now matches these typed records.
Proven end-to-end in the VM: 'cubec query klog' returns the kernel-log
index record (doc_type=klog) published by cube-os-klog.sh, and
'cubec query fn' returns the 8 pre-existing typed program records.
This commit is contained in:
@@ -939,6 +939,62 @@ impl Session {
|
||||
store.delete_raw(&coord);
|
||||
Ok(format!("ok: deleted {}", coord.pack_u32()))
|
||||
}
|
||||
// --- Enveloped coordinate API (PDF Package 2: `put(C,Z,Y,X,
|
||||
// bytes, flags)`). Unlike `rawput`, this writes a full record
|
||||
// with a `CubeHeader`, so the record carries metatags
|
||||
// (doc_type / title) that the associative `query` verb can
|
||||
// target by event type. `rawput` stays for bulk/append payloads
|
||||
// (e.g. the rolling kernel-log window) that do not need header
|
||||
// metadata; `put` is for addressed, discoverable records. ---
|
||||
"put" => {
|
||||
let c = parse_u8(it.next(), "put needs <c>")?;
|
||||
let z = parse_u8(it.next(), "put needs <z>")?;
|
||||
let y = parse_u8(it.next(), "put needs <y>")?;
|
||||
let x = parse_u8(it.next(), "put needs <x>")?;
|
||||
let hex = it
|
||||
.next()
|
||||
.ok_or_else(|| "put needs <hex-bytes>".to_string())?;
|
||||
let val = hex_decode(hex).ok_or_else(|| "put: value must be hex".to_string())?;
|
||||
let coord = Czyx::new(c, z, y, x);
|
||||
// Optional metatags: `doc_type=<t>` and/or `title=<t>` may
|
||||
// follow. These are what `query <doc_type>` matches on.
|
||||
let mut header = CubeHeader::new();
|
||||
while let Some(tok) = it.next() {
|
||||
if let Some(v) = tok.strip_prefix("doc_type=") {
|
||||
header.doc_type = Some(v.to_string());
|
||||
} else if let Some(v) = tok.strip_prefix("title=") {
|
||||
header.title = Some(v.to_string());
|
||||
} else {
|
||||
return Err(format!("put: unknown option {tok}"));
|
||||
}
|
||||
}
|
||||
header.created_at = Some(
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_secs())
|
||||
.unwrap_or(0),
|
||||
);
|
||||
header.refresh_flags();
|
||||
let doc_type_dbg = header.doc_type.clone();
|
||||
if let Some(txn) = self.txn.as_mut() {
|
||||
txn.ops.push(TxnOp {
|
||||
coord,
|
||||
put: Some((val, header)),
|
||||
});
|
||||
Ok(format!(
|
||||
"buffered put {} (doc_type={:?}) — commit to apply",
|
||||
coord.pack_u32(),
|
||||
doc_type_dbg
|
||||
))
|
||||
} else {
|
||||
store.put_record(coord, &header, &val);
|
||||
Ok(format!(
|
||||
"ok: wrote {} (doc_type={:?})",
|
||||
coord.pack_u32(),
|
||||
doc_type_dbg
|
||||
))
|
||||
}
|
||||
}
|
||||
"rawkeys" => {
|
||||
let ks: Vec<String> = store
|
||||
.keys()
|
||||
|
||||
Reference in New Issue
Block a user