On this page· 3
Take part of a pack
- file format 6
- Rust SDK 1.0 preview
Goal: write a smaller pack holding some of the records of a larger one.
Take part of a pack with a filtered read followed by write.
- 1. Read the pack through a
Filter, as in example 3. - 2. Decide what to do about relations whose far end was not kept. Nothing adds the missing entity; add a stub by hand if one is wanted.
- 3. Pass the kept records to
write, as in example 1.
Needs the Rust SDK
filter_and_views.rsrust
// Read only some records, and look at entities and relations
// through typed views.
use plxi_sdk::{write, Filter, OpenOptions, Pack, Record};
fn main() -> Result<(), plxi_sdk::Error> {
let lines = [
r#"{"k":"ent","id":"e1","t":"Person","name":"Ada","salience":0.5}"#,
r#"{"k":"ent","id":"e2","t":"Person"}"#,
r#"{"k":"ent","id":"p1","t":"Place","name":"London"}"#,
r#"{"k":"rel","src":"e1","tgt":"p1","kind":"lives_in"}"#,
];
let records = lines
.iter()
.map(|l| Record::from_json_line(l))
.collect::<Result<Vec<_>, _>>()?;
let bytes = write(&records, None)?;
let pack = Pack::from_bytes(bytes, OpenOptions::default())?;
// By kind.
let only_entities = Filter::new().with_kinds(["ent"]);
let mut names = Vec::new();
for record in pack.records_filtered(&only_entities) {
let record = record?;
let entity = record
.as_entity()
.expect("an `ent` record has an entity view");
let name = entity.name.map(str::to_string);
names.push((entity.id.to_string(), entity.t.to_string(), name));
}
assert_eq!(names.len(), 3);
let first = (
"e1".to_string(),
"Person".to_string(),
Some("Ada".to_string()),
);
assert_eq!(names[0], first);
// By primary-id prefix. A relation's primary id is "src|tgt|kind".
let about_e1 = Filter::new().with_id_prefix("e1");
let ids: Vec<String> = pack
.records_filtered(&about_e1)
.map(|r| r.map(|rec| rec.primary_id().to_string()))
.collect::<Result<_, _>>()?;
assert_eq!(ids, ["e1", "e1|p1|lives_in"]);
// Both conditions at once: every field that is set must match.
let both = Filter::new().with_kinds(["rel"]).with_id_prefix("e1");
let rels: Vec<Record> =
pack.records_filtered(&both).collect::<Result<_, _>>()?;
let view = rels[0]
.as_relation()
.expect("a `rel` record has a relation view");
assert_eq!((view.src, view.tgt, view.kind), ("e1", "p1", "lives_in"));
println!("{} -[{}]-> {}", view.src, view.kind, view.tgt);
Ok(())
}
Output
e1 -[lives_in]-> p1Ran with cargo run --example 03_filter_and_views · exit status 0
A run that kept the lathe and its relation, and added a stub for the manual by hand, wrote this pack:
| Record |
|---|
{"k":"ent","v":1,"id":"lathe","t":"Tool","name":"Lathe","stub":false} |
{"k":"ent","v":1,"id":"manual","t":"Doc","stub":true} |
{"k":"rel","v":1,"src":"lathe","tgt":"manual","kind":"described_by"} |
- A filter follows no relations. It matches each record on its own kind and primary id.
- A hand-made stub is an ordinary
entrecord with"stub":true. Merged later with the full record, it gives way to it. - This route carries no binary data across:
writeis givenNone, so embedding rows in the source pack are not in the result.
| Part | What a filtered write does |
|---|---|
| Selection | by kind and by primary-id prefix |
| Entities one relation outside the selection | not added; add a stub by hand |
| Relations | kept if they match the filter, whether or not both ends were kept |
| Binary data | not carried: write is given None |