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.

Steps

  1. 1. Read the pack through a Filter, as in example 3.
  2. 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. 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]-> p1

Ran 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:

Table 1. A smaller pack: 3 records
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"}

Things to know

  • A filter follows no relations. It matches each record on its own kind and primary id.
  • A hand-made stub is an ordinary ent record with "stub":true. Merged later with the full record, it gives way to it.
  • This route carries no binary data across: write is given None, so embedding rows in the source pack are not in the result.
Table 2. What a filtered write keeps: 4
PartWhat a filtered write does
Selectionby kind and by primary-id prefix
Entities one relation outside the selectionnot added; add a stub by hand
Relationskept if they match the filter, whether or not both ends were kept
Binary datanot carried: write is given None

Reference

Sections