Move Schema from mentat_db to mentat_core, improve API. (#290)
* Move Schema from mentat_db to mentat_core. * Define SchemaMap in terms of Entid, not i64. * Add Schema::{is_attribute,identifies_attribute}. * Add pointer to #291. * Don't pass around 64-bit pointers to 64-bit integers.
This commit is contained in:
parent
2e303f4837
commit
a87e5a3ec7
6 changed files with 116 additions and 68 deletions
|
@ -9,6 +9,8 @@
|
|||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
extern crate ordered_float;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use self::ordered_float::OrderedFloat;
|
||||
|
||||
/// Core types defining a Mentat knowledge base.
|
||||
|
@ -173,6 +175,64 @@ impl Default for Attribute {
|
|||
}
|
||||
}
|
||||
|
||||
/// Map `String` idents (`:db/ident`) to positive integer entids (`1`).
|
||||
/// TODO: these should all be parsed into NamespacedKeywords on entry. #291.
|
||||
pub type IdentMap = BTreeMap<String, Entid>;
|
||||
|
||||
/// Map positive integer entids (`1`) to `String` idents (`:db/ident`).
|
||||
pub type EntidMap = BTreeMap<Entid, String>;
|
||||
|
||||
/// Map attribute entids to `Attribute` instances.
|
||||
pub type SchemaMap = BTreeMap<Entid, Attribute>;
|
||||
|
||||
/// Represents a Mentat schema.
|
||||
///
|
||||
/// Maintains the mapping between string idents and positive integer entids; and exposes the schema
|
||||
/// flags associated to a given entid (equivalently, ident).
|
||||
///
|
||||
/// TODO: consider a single bi-directional map instead of separate ident->entid and entid->ident
|
||||
/// maps.
|
||||
#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialOrd, PartialEq)]
|
||||
pub struct Schema {
|
||||
/// Map entid->ident.
|
||||
///
|
||||
/// Invariant: is the inverse map of `ident_map`.
|
||||
pub entid_map: EntidMap,
|
||||
|
||||
/// Map ident->entid.
|
||||
///
|
||||
/// Invariant: is the inverse map of `entid_map`.
|
||||
pub ident_map: IdentMap,
|
||||
|
||||
/// Map entid->attribute flags.
|
||||
///
|
||||
/// Invariant: key-set is the same as the key-set of `entid_map` (equivalently, the value-set of
|
||||
/// `ident_map`).
|
||||
pub schema_map: SchemaMap,
|
||||
}
|
||||
|
||||
impl Schema {
|
||||
pub fn get_ident(&self, x: Entid) -> Option<&String> {
|
||||
self.entid_map.get(&x)
|
||||
}
|
||||
|
||||
pub fn get_entid(&self, x: &String) -> Option<Entid> {
|
||||
self.ident_map.get(x).map(|x| *x)
|
||||
}
|
||||
|
||||
pub fn attribute_for_entid(&self, x: Entid) -> Option<&Attribute> {
|
||||
self.schema_map.get(&x)
|
||||
}
|
||||
|
||||
pub fn is_attribute(&self, x: Entid) -> bool {
|
||||
self.schema_map.contains_key(&x)
|
||||
}
|
||||
|
||||
pub fn identifies_attribute(&self, x: &String) -> bool {
|
||||
self.get_entid(x).map(|e| self.is_attribute(e)).unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
|
|
@ -18,8 +18,13 @@ use entids;
|
|||
use db::TypedSQLValue;
|
||||
use mentat_tx::entities::Entity;
|
||||
use mentat_tx_parser;
|
||||
use mentat_core::TypedValue;
|
||||
use types::{IdentMap, Partition, PartitionMap, Schema};
|
||||
use mentat_core::{
|
||||
IdentMap,
|
||||
Schema,
|
||||
TypedValue,
|
||||
};
|
||||
use schema::SchemaBuilding;
|
||||
use types::{Partition, PartitionMap};
|
||||
use values;
|
||||
|
||||
/// The first transaction ID applied to the knowledge base.
|
||||
|
|
28
db/src/db.rs
28
db/src/db.rs
|
@ -23,10 +23,20 @@ use ::{repeat_values, to_namespaced_keyword};
|
|||
use bootstrap;
|
||||
use edn::types::Value;
|
||||
use entids;
|
||||
use mentat_core::{
|
||||
Attribute,
|
||||
AttributeBitFlags,
|
||||
Entid,
|
||||
IdentMap,
|
||||
Schema,
|
||||
TypedValue,
|
||||
ValueType,
|
||||
};
|
||||
use mentat_tx::entities as entmod;
|
||||
use mentat_tx::entities::{Entity, OpType};
|
||||
use errors::{ErrorKind, Result, ResultExt};
|
||||
use types::{Attribute, AttributeBitFlags, DB, Entid, IdentMap, Partition, PartitionMap, Schema, TypedValue, ValueType};
|
||||
use types::{DB, Partition, PartitionMap};
|
||||
use schema::SchemaBuilding;
|
||||
|
||||
pub fn new_connection<T>(uri: T) -> rusqlite::Result<rusqlite::Connection> where T: AsRef<Path> {
|
||||
let conn = match uri.as_ref().to_string_lossy().len() {
|
||||
|
@ -446,7 +456,7 @@ impl DB {
|
|||
(&ValueType::Keyword, tv @ TypedValue::Keyword(_)) => Ok(tv),
|
||||
// Ref coerces a little: we interpret some things depending on the schema as a Ref.
|
||||
(&ValueType::Ref, TypedValue::Long(x)) => Ok(TypedValue::Ref(x)),
|
||||
(&ValueType::Ref, TypedValue::Keyword(ref x)) => self.schema.require_entid(&x.to_string()).map(|&entid| TypedValue::Ref(entid)),
|
||||
(&ValueType::Ref, TypedValue::Keyword(ref x)) => self.schema.require_entid(&x.to_string()).map(|entid| TypedValue::Ref(entid)),
|
||||
// Otherwise, we have a type mismatch.
|
||||
(value_type, _) => bail!(ErrorKind::BadEDNValuePair(value.clone(), value_type.clone())),
|
||||
}
|
||||
|
@ -535,7 +545,7 @@ impl DB {
|
|||
/* added0 */ bool,
|
||||
/* flags0 */ u8)>> = chunk.map(|&(e, a, ref typed_value, added)| {
|
||||
count += 1;
|
||||
let attribute: &Attribute = self.schema.require_attribute_for_entid(&a)?;
|
||||
let attribute: &Attribute = self.schema.require_attribute_for_entid(a)?;
|
||||
|
||||
// Now we can represent the typed value as an SQL value.
|
||||
let (value, value_type_tag): (ToSqlOutput, i32) = typed_value.to_sql_value_pair();
|
||||
|
@ -725,15 +735,15 @@ impl DB {
|
|||
|
||||
let e: i64 = match e_ {
|
||||
&entmod::Entid::Entid(ref e__) => *e__,
|
||||
&entmod::Entid::Ident(ref e__) => *self.schema.require_entid(&e__.to_string())?,
|
||||
&entmod::Entid::Ident(ref e__) => self.schema.require_entid(&e__.to_string())?,
|
||||
};
|
||||
|
||||
let a: i64 = match a_ {
|
||||
&entmod::Entid::Entid(ref a__) => *a__,
|
||||
&entmod::Entid::Ident(ref a__) => *self.schema.require_entid(&a__.to_string())?,
|
||||
&entmod::Entid::Ident(ref a__) => self.schema.require_entid(&a__.to_string())?,
|
||||
};
|
||||
|
||||
let attribute: &Attribute = self.schema.require_attribute_for_entid(&a)?;
|
||||
let attribute: &Attribute = self.schema.require_attribute_for_entid(a)?;
|
||||
if attribute.fulltext {
|
||||
bail!(ErrorKind::NotYetImplemented(format!("Transacting :db/fulltext entities is not yet implemented: {:?}", entity)))
|
||||
}
|
||||
|
@ -760,15 +770,15 @@ impl DB {
|
|||
|
||||
let e: i64 = match e_ {
|
||||
&entmod::Entid::Entid(ref e__) => *e__,
|
||||
&entmod::Entid::Ident(ref e__) => *self.schema.require_entid(&e__.to_string())?,
|
||||
&entmod::Entid::Ident(ref e__) => self.schema.require_entid(&e__.to_string())?,
|
||||
};
|
||||
|
||||
let a: i64 = match a_ {
|
||||
&entmod::Entid::Entid(ref a__) => *a__,
|
||||
&entmod::Entid::Ident(ref a__) => *self.schema.require_entid(&a__.to_string())?,
|
||||
&entmod::Entid::Ident(ref a__) => self.schema.require_entid(&a__.to_string())?,
|
||||
};
|
||||
|
||||
let attribute: &Attribute = self.schema.require_attribute_for_entid(&a)?;
|
||||
let attribute: &Attribute = self.schema.require_attribute_for_entid(a)?;
|
||||
if attribute.fulltext {
|
||||
bail!(ErrorKind::NotYetImplemented(format!("Transacting :db/fulltext entities is not yet implemented: {:?}", entity)))
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ impl Transactions {
|
|||
|
||||
/// Convert a numeric entid to an ident `Entid` if possible, otherwise a numeric `Entid`.
|
||||
fn to_entid(db: &DB, entid: i64) -> Entid {
|
||||
db.schema.get_ident(&entid).and_then(|ident| to_namespaced_keyword(&ident)).map_or(Entid::Entid(entid), Entid::Ident)
|
||||
db.schema.get_ident(entid).and_then(|ident| to_namespaced_keyword(&ident)).map_or(Entid::Entid(entid), Entid::Ident)
|
||||
}
|
||||
|
||||
/// Return the set of datoms in the store, ordered by (e, a, v, tx), but not including any datoms of
|
||||
|
|
|
@ -12,7 +12,16 @@
|
|||
|
||||
use entids;
|
||||
use errors::*;
|
||||
use types::{Attribute, Entid, EntidMap, IdentMap, Schema, SchemaMap, TypedValue, ValueType};
|
||||
use mentat_core::{
|
||||
Attribute,
|
||||
Entid,
|
||||
EntidMap,
|
||||
IdentMap,
|
||||
Schema,
|
||||
SchemaMap,
|
||||
TypedValue,
|
||||
ValueType,
|
||||
};
|
||||
|
||||
/// Return `Ok(())` if `schema_map` defines a valid Mentat schema.
|
||||
fn validate_schema_map(entid_map: &EntidMap, schema_map: &SchemaMap) -> Result<()> {
|
||||
|
@ -40,33 +49,30 @@ fn validate_schema_map(entid_map: &EntidMap, schema_map: &SchemaMap) -> Result<(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
impl Schema {
|
||||
pub fn get_ident(&self, x: &Entid) -> Option<&String> {
|
||||
self.entid_map.get(x)
|
||||
pub trait SchemaBuilding {
|
||||
fn require_ident(&self, entid: Entid) -> Result<&String>;
|
||||
fn require_entid(&self, ident: &String) -> Result<Entid>;
|
||||
fn require_attribute_for_entid(&self, entid: Entid) -> Result<&Attribute>;
|
||||
fn from_ident_map_and_schema_map(ident_map: IdentMap, schema_map: SchemaMap) -> Result<Schema>;
|
||||
fn from_ident_map_and_triples<U>(ident_map: IdentMap, assertions: U) -> Result<Schema>
|
||||
where U: IntoIterator<Item=(String, String, TypedValue)>;
|
||||
}
|
||||
|
||||
pub fn get_entid(&self, x: &String) -> Option<&Entid> {
|
||||
self.ident_map.get(x)
|
||||
impl SchemaBuilding for Schema {
|
||||
fn require_ident(&self, entid: Entid) -> Result<&String> {
|
||||
self.get_ident(entid).ok_or(ErrorKind::UnrecognizedEntid(entid).into())
|
||||
}
|
||||
|
||||
pub fn attribute_for_entid(&self, x: &Entid) -> Option<&Attribute> {
|
||||
self.schema_map.get(x)
|
||||
}
|
||||
|
||||
pub fn require_ident(&self, entid: &Entid) -> Result<&String> {
|
||||
self.get_ident(&entid).ok_or(ErrorKind::UnrecognizedEntid(*entid).into())
|
||||
}
|
||||
|
||||
pub fn require_entid(&self, ident: &String) -> Result<&Entid> {
|
||||
fn require_entid(&self, ident: &String) -> Result<Entid> {
|
||||
self.get_entid(&ident).ok_or(ErrorKind::UnrecognizedIdent(ident.clone()).into())
|
||||
}
|
||||
|
||||
pub fn require_attribute_for_entid(&self, entid: &Entid) -> Result<&Attribute> {
|
||||
self.attribute_for_entid(entid).ok_or(ErrorKind::UnrecognizedEntid(*entid).into())
|
||||
fn require_attribute_for_entid(&self, entid: Entid) -> Result<&Attribute> {
|
||||
self.attribute_for_entid(entid).ok_or(ErrorKind::UnrecognizedEntid(entid).into())
|
||||
}
|
||||
|
||||
/// Create a valid `Schema` from the constituent maps.
|
||||
pub fn from(ident_map: IdentMap, schema_map: SchemaMap) -> Result<Schema> {
|
||||
fn from_ident_map_and_schema_map(ident_map: IdentMap, schema_map: SchemaMap) -> Result<Schema> {
|
||||
let entid_map: EntidMap = ident_map.iter().map(|(k, v)| (v.clone(), k.clone())).collect();
|
||||
|
||||
validate_schema_map(&entid_map, &schema_map)?;
|
||||
|
@ -79,7 +85,7 @@ impl Schema {
|
|||
}
|
||||
|
||||
/// Turn vec![(String(:ident), String(:key), TypedValue(:value)), ...] into a Mentat `Schema`.
|
||||
pub fn from_ident_map_and_triples<U>(ident_map: IdentMap, assertions: U) -> Result<Schema>
|
||||
fn from_ident_map_and_triples<U>(ident_map: IdentMap, assertions: U) -> Result<Schema>
|
||||
where U: IntoIterator<Item=(String, String, TypedValue)>{
|
||||
let mut schema_map = SchemaMap::new();
|
||||
for (ref symbolic_ident, ref symbolic_attr, ref value) in assertions.into_iter() {
|
||||
|
@ -167,6 +173,6 @@ impl Schema {
|
|||
}
|
||||
};
|
||||
|
||||
Schema::from(ident_map.clone(), schema_map)
|
||||
Schema::from_ident_map_and_schema_map(ident_map.clone(), schema_map)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ pub use self::mentat_core::{
|
|||
TypedValue,
|
||||
Attribute,
|
||||
AttributeBitFlags,
|
||||
Schema,
|
||||
};
|
||||
|
||||
/// Represents one partition of the entid space.
|
||||
|
@ -40,40 +41,6 @@ impl Partition {
|
|||
|
||||
/// Map partition names to `Partition` instances.
|
||||
pub type PartitionMap = BTreeMap<String, Partition>;
|
||||
/// Map `String` idents (`:db/ident`) to positive integer entids (`1`).
|
||||
pub type IdentMap = BTreeMap<String, Entid>;
|
||||
|
||||
/// Map positive integer entids (`1`) to `String` idents (`:db/ident`).
|
||||
pub type EntidMap = BTreeMap<Entid, String>;
|
||||
|
||||
/// Map attribute entids to `Attribute` instances.
|
||||
pub type SchemaMap = BTreeMap<i64, Attribute>;
|
||||
|
||||
/// Represents a Mentat schema.
|
||||
///
|
||||
/// Maintains the mapping between string idents and positive integer entids; and exposes the schema
|
||||
/// flags associated to a given entid (equivalently, ident).
|
||||
///
|
||||
/// TODO: consider a single bi-directional map instead of separate ident->entid and entid->ident
|
||||
/// maps.
|
||||
#[derive(Clone,Debug,Default,Eq,Hash,Ord,PartialOrd,PartialEq)]
|
||||
pub struct Schema {
|
||||
/// Map entid->ident.
|
||||
///
|
||||
/// Invariant: is the inverse map of `ident_map`.
|
||||
pub entid_map: EntidMap,
|
||||
|
||||
/// Map ident->entid.
|
||||
///
|
||||
/// Invariant: is the inverse map of `entid_map`.
|
||||
pub ident_map: IdentMap,
|
||||
|
||||
/// Map entid->attribute flags.
|
||||
///
|
||||
/// Invariant: key-set is the same as the key-set of `entid_map` (equivalently, the value-set of
|
||||
/// `ident_map`).
|
||||
pub schema_map: SchemaMap,
|
||||
}
|
||||
|
||||
/// Represents the metadata required to query from, or apply transactions to, a Mentat store.
|
||||
///
|
||||
|
|
Loading…
Reference in a new issue