Proposal: in-query retract and assert #215

Open
opened 2020-08-06 16:56:47 +00:00 by gburd · 0 comments
gburd commented 2020-08-06 16:56:47 +00:00 (Migrated from github.com)

A common pattern I've encountered is:

let link_title = ip.get_entid(&kw!(:link/title)).unwrap();

let results = ip.q_once(r#"
    [:find ?e ?t2
     :where [?e :link/title ?t1]
            [?e :link/title ?t2]
            [(unpermute ?t1 ?t2)]]
"#, None).into_rel_result()?;

if !results.is_empty() {
    let mut builder = TermBuilder::new();
    for row in results.into_iter() {
        let mut r = row.into_iter();
        let e = r.next().and_then(|e| e.into_known_entid()).expect("entity");
        let obsolete = r.next().expect("value");
        builder.retract(e, link_title, obsolete)?;
    }
    ip.transact_builder(builder)?;
}

That is: query to build up retractions and/or assertions, then transact those.

Datomic follows this model easily: it's loosely typed, data is local and in memory, etc. etc.

It's slightly less comfortable in Rust because of the rigid type system: we can't easily make builder ergonomic, and the caller needs to do some ritual (into_rel_result, consuming the results, etc.) to make this happen.

It's likely to be even more uncomfortable, and perhaps a little slow, from other languages.

We might instead choose to borrow from SPARQL UPDATE and extend our syntax to do this inline:

PREFIX foaf:  <http://xmlns.com/foaf/0.1/>

WITH <http://example/addresses>
DELETE { ?person foaf:givenName 'Bill' }
INSERT { ?person foaf:givenName 'William' }
WHERE
  { ?person foaf:givenName 'Bill'
  } 

which for Mentat might look exactly like a :find query with different operands:

[:retract (?person :person/name "Bill")
 :assert (?person :person/name "William")
 :where [?person :person/name "Bill"]]

This could be evaluated in one of two ways:

  • As a special kind of projection that does the ritual consume/build/transact work for you. This is expensive — values will be pulled into memory, collected, then serialized for SQL and transacted — but it's relatively little work.
  • By translation into SQL. In part this will encode some of the work the transactor already does. More development work, but more production-grade.
A common pattern I've encountered is: ```rust let link_title = ip.get_entid(&kw!(:link/title)).unwrap(); let results = ip.q_once(r#" [:find ?e ?t2 :where [?e :link/title ?t1] [?e :link/title ?t2] [(unpermute ?t1 ?t2)]] "#, None).into_rel_result()?; if !results.is_empty() { let mut builder = TermBuilder::new(); for row in results.into_iter() { let mut r = row.into_iter(); let e = r.next().and_then(|e| e.into_known_entid()).expect("entity"); let obsolete = r.next().expect("value"); builder.retract(e, link_title, obsolete)?; } ip.transact_builder(builder)?; } ``` That is: query to build up retractions and/or assertions, then transact those. Datomic follows this model easily: it's loosely typed, data is local and in memory, etc. etc. It's slightly less comfortable in Rust because of the rigid type system: we can't easily make `builder` ergonomic, and the caller needs to do some ritual (`into_rel_result`, consuming the results, etc.) to make this happen. It's likely to be even more uncomfortable, and perhaps a little slow, from other languages. We might instead choose to borrow from [SPARQL UPDATE](https://www.w3.org/TR/sparql11-update/) and extend our syntax to do this inline: ```sparql PREFIX foaf: <http://xmlns.com/foaf/0.1/> WITH <http://example/addresses> DELETE { ?person foaf:givenName 'Bill' } INSERT { ?person foaf:givenName 'William' } WHERE { ?person foaf:givenName 'Bill' } ``` which for Mentat might look exactly like a `:find` query with different operands: ```edn [:retract (?person :person/name "Bill") :assert (?person :person/name "William") :where [?person :person/name "Bill"]] ``` This could be evaluated in one of two ways: - As a special kind of projection that does the ritual consume/build/transact work for you. This is expensive — values will be pulled into memory, collected, then serialized for SQL and transacted — but it's relatively little work. - By translation into SQL. In part this will encode some of the work the transactor already does. More development work, but more production-grade.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: greg/mentat#215
No description provided.