Skip to content

Commit

Permalink
Remove document meta (#472)
Browse files Browse the repository at this point in the history
* Remove DocumentMeta

* Correct schema => schema_id

* fmt

* Update CHANGELOG

* Fix import order

* fmt
  • Loading branch information
sandreae committed Jan 13, 2023
1 parent bf32fda commit 9d54832
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 95 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -13,6 +13,7 @@ Highlights are marked with a pancake 🥞

- Remove `VerifiedOperation` [#465](https://github.com/p2panda/p2panda/pull/465) `rs`
- Better docs for `Document` [#470](https://github.com/p2panda/p2panda/pull/470) `rs`
- Remove `DocumentMeta` [#472](https://github.com/p2panda/p2panda/pull/472) `rs`

## [0.6.0] 🥞

Expand Down
143 changes: 61 additions & 82 deletions p2panda-rs/src/document/document.rs
Expand Up @@ -5,10 +5,10 @@ use std::fmt::{Debug, Display};

use crate::document::error::DocumentBuilderError;
use crate::document::materialization::{build_graph, reduce};
use crate::document::{DocumentId, DocumentView, DocumentViewId};
use crate::document::{DocumentId, DocumentView, DocumentViewFields, DocumentViewId};
use crate::identity::PublicKey;
use crate::operation::traits::{AsOperation, WithOperationId, WithPublicKey};
use crate::operation::{Operation, OperationId};
use crate::operation::{Operation, OperationId, OperationValue};
use crate::schema::SchemaId;
use crate::Human;

Expand All @@ -18,19 +18,6 @@ pub type IsEdited = bool;
/// Flag to indicate if document was deleted by at least one author.
pub type IsDeleted = bool;

/// Metadata attached to a document as well as all of it's operations.
#[derive(Debug, Clone, Default)]
pub struct DocumentMeta {
/// Flag indicating if document was deleted.
deleted: IsDeleted,

/// Flag indicating if document was edited.
edited: IsEdited,

/// List of operations this document consists of.
operations: Vec<(OperationId, Operation, PublicKey)>,
}

/// High-level datatype representing data published to the p2panda network as key-value pairs.
///
/// Documents are multi-writer and have automatic conflict resolution strategies which produce deterministic
Expand All @@ -48,12 +35,26 @@ pub struct DocumentMeta {
/// See module docs for example uses.
#[derive(Debug, Clone)]
pub struct Document {
/// The id for this document.
id: DocumentId,

/// The data this document contains as key-value pairs.
fields: Option<DocumentViewFields>,

/// The id of the schema this document follows.
schema_id: SchemaId,

/// The id of the current view of this document.
view_id: DocumentViewId,

/// The public key of the author who created this document.
author: PublicKey,
schema: SchemaId,
view: Option<DocumentView>,
meta: DocumentMeta,

/// Flag indicating if document was deleted.
deleted: IsDeleted,

/// Flag indicating if document was edited.
edited: IsEdited,
}

impl Document {
Expand All @@ -62,6 +63,14 @@ impl Document {
&self.id
}

/// Get the value for a field on this document.
pub fn get(&self, key: &str) -> Option<&OperationValue> {
if let Some(fields) = self.fields() {
return fields.get(key).map(|view_value| view_value.value());
}
None
}

/// Get the document view id.
pub fn view_id(&self) -> &DocumentViewId {
&self.view_id
Expand All @@ -73,28 +82,30 @@ impl Document {
}

/// Get the document schema.
pub fn schema(&self) -> &SchemaId {
&self.schema
pub fn schema_id(&self) -> &SchemaId {
&self.schema_id
}

/// Get the view of this document.
pub fn view(&self) -> Option<&DocumentView> {
self.view.as_ref()
/// The current document view for this document. Returns None if this document
/// has been deleted.
pub fn view(&self) -> Option<DocumentView> {
self.fields()
.map(|fields| DocumentView::new(self.view_id(), fields))
}

/// Get the operations contained in this document.
pub fn operations(&self) -> &Vec<(OperationId, Operation, PublicKey)> {
&self.meta.operations
/// Get the fields of this document.
pub fn fields(&self) -> Option<&DocumentViewFields> {
self.fields.as_ref()
}

/// Returns true if this document has applied an UPDATE operation.
pub fn is_edited(&self) -> IsEdited {
self.meta.edited
self.edited
}

/// Returns true if this document has processed a DELETE operation.
pub fn is_deleted(&self) -> IsDeleted {
self.meta.deleted
self.deleted
}
}

Expand Down Expand Up @@ -193,7 +204,7 @@ impl DocumentBuilder {
}?;

// Get the document schema
let schema = create_operation.schema_id();
let schema_id = create_operation.schema_id();

// Get the document author (or rather, the public key of the author who created this
// document)
Expand All @@ -203,7 +214,7 @@ impl DocumentBuilder {
let schema_error = self
.operations()
.iter()
.any(|(_, operation, _)| operation.schema_id() != schema);
.any(|(_, operation, _)| operation.schema_id() != schema_id);

if schema_error {
return Err(DocumentBuilderError::OperationSchemaNotMatching);
Expand All @@ -230,33 +241,19 @@ impl DocumentBuilder {
.collect();

// Reduce the sorted operations into a single key value map
let (view, is_edited, is_deleted) = reduce(&sorted_graph_data.sorted()[..]);

// Construct document meta data
let meta = DocumentMeta {
edited: is_edited,
deleted: is_deleted,
operations: sorted_graph_data.sorted(),
};
let (fields, is_edited, is_deleted) = reduce(&sorted_graph_data.sorted()[..]);

// Construct the document view id
let document_view_id = DocumentViewId::new(&graph_tips);

// Construct the document view, from the reduced values and the document view id
let document_view = if is_deleted {
None
} else {
// Unwrap as documents which aren't deleted will have a view
Some(DocumentView::new(&document_view_id, &view.unwrap()))
};

Ok(Document {
id: document_id,
view_id: document_view_id,
schema,
schema_id,
author,
view: document_view,
meta,
fields,
edited: is_edited,
deleted: is_deleted,
})
}
}
Expand Down Expand Up @@ -481,12 +478,6 @@ mod tests {
// Document should resolve to expected value
let document = document.unwrap();

let operation_order: Vec<OperationId> = document
.operations()
.iter()
.map(|(id, _, _)| id.to_owned())
.collect();

let mut exp_result = DocumentViewFields::new();
exp_result.insert(
"name",
Expand All @@ -498,28 +489,19 @@ mod tests {

let document_id = DocumentId::new(&panda_entry_1.hash().into());
let expected_graph_tips: Vec<OperationId> = vec![penguin_entry_3.hash().into()];
let expected_op_order: Vec<OperationId> = vec![
panda_entry_1.clone(),
panda_entry_2,
penguin_entry_1,
penguin_entry_2,
penguin_entry_3,
]
.iter()
.map(|entry| entry.hash().into())
.collect();

assert_eq!(document.view().unwrap().get("name"), exp_result.get("name"));
assert_eq!(
document.fields().unwrap().get("name"),
exp_result.get("name")
);
assert!(document.is_edited());
assert!(!document.is_deleted());
assert_eq!(document.author(), &panda.public_key());
assert_eq!(document.schema(), schema.id());
assert_eq!(operation_order, expected_op_order);
assert_eq!(document.schema_id(), schema.id());
assert_eq!(document.view_id().graph_tips(), expected_graph_tips);
assert_eq!(document.id(), &document_id);

// Multiple replicas receiving operations in different orders should resolve to same value.

let replica_1: Document = vec![
operations[4],
operations[3],
Expand All @@ -541,20 +523,19 @@ mod tests {
.unwrap();

assert_eq!(
replica_1.view().unwrap().get("name"),
replica_1.fields().unwrap().get("name"),
exp_result.get("name")
);
assert!(replica_1.is_edited());
assert!(!replica_1.is_deleted());
assert_eq!(replica_1.author(), &panda.public_key());
assert_eq!(replica_1.schema(), schema.id());
assert_eq!(operation_order, expected_op_order);
assert_eq!(replica_1.schema_id(), schema.id());
assert_eq!(replica_1.view_id().graph_tips(), expected_graph_tips);
assert_eq!(replica_1.id(), &document_id);

assert_eq!(
replica_1.view().unwrap().get("name"),
replica_2.view().unwrap().get("name")
replica_1.fields().unwrap().get("name"),
replica_2.fields().unwrap().get("name")
);
assert_eq!(replica_1.id(), replica_2.id());
assert_eq!(
Expand Down Expand Up @@ -654,7 +635,7 @@ mod tests {
.unwrap();

assert!(document.is_deleted());
assert!(document.view().is_none());
assert!(document.fields().is_none());
}

#[rstest]
Expand All @@ -672,9 +653,7 @@ mod tests {

#[rstest]
#[tokio::test]
async fn builds_specific_document_view(
#[with(vec![("name".to_string(), FieldType::String)])] schema: Schema,
) {
async fn fields(#[with(vec![("name".to_string(), FieldType::String)])] schema: Schema) {
let mut operations = Vec::new();

let panda = KeyPair::new().public_key().to_owned();
Expand Down Expand Up @@ -735,7 +714,7 @@ mod tests {
document_builder
.build_to_view_id(Some(DocumentViewId::new(&[operation_1_id])))
.unwrap()
.view()
.fields()
.unwrap()
.get("name")
.unwrap()
Expand All @@ -747,7 +726,7 @@ mod tests {
document_builder
.build_to_view_id(Some(DocumentViewId::new(&[operation_2_id.clone()])))
.unwrap()
.view()
.fields()
.unwrap()
.get("name")
.unwrap()
Expand All @@ -759,7 +738,7 @@ mod tests {
document_builder
.build_to_view_id(Some(DocumentViewId::new(&[operation_3_id.clone()])))
.unwrap()
.view()
.fields()
.unwrap()
.get("name")
.unwrap()
Expand All @@ -771,7 +750,7 @@ mod tests {
document_builder
.build_to_view_id(Some(DocumentViewId::new(&[operation_2_id, operation_3_id])))
.unwrap()
.view()
.fields()
.unwrap()
.get("name")
.unwrap()
Expand Down
27 changes: 14 additions & 13 deletions p2panda-rs/src/test_utils/db/stores/document.rs
Expand Up @@ -61,7 +61,7 @@ impl DocumentStore for MemoryStore {
.insert(document.id().to_owned(), document.to_owned());

if !document.is_deleted() {
self.insert_document_view(document.view().unwrap(), document.schema())
self.insert_document_view(&document.view().unwrap(), document.schema_id())
.await?;
}

Expand All @@ -86,7 +86,7 @@ impl DocumentStore for MemoryStore {
.get(id)
.map(|document| document.to_owned())
{
Some(document) => Ok(document.view().map(|view| view.to_owned())),
Some(document) => Ok(document.view()),
None => Ok(None),
}
}
Expand All @@ -104,8 +104,8 @@ impl DocumentStore for MemoryStore {
.lock()
.unwrap()
.iter()
.filter(|(_, document)| document.schema() == schema_id)
.filter_map(|(_, document)| document.view().cloned())
.filter(|(_, document)| document.schema_id() == schema_id)
.filter_map(|(_, document)| document.view())
.collect();

Ok(documents)
Expand All @@ -118,6 +118,7 @@ mod tests {

use rstest::rstest;

use crate::document::materialization::build_graph;
use crate::document::{
Document, DocumentBuilder, DocumentView, DocumentViewFields, DocumentViewId,
};
Expand Down Expand Up @@ -363,16 +364,16 @@ mod tests {
.await
.unwrap();

let document = Document::try_from(&operations).unwrap();

let mut current_operations = Vec::new();
let document_builder = DocumentBuilder::from(&operations);
let ordered_operations = build_graph(&document_builder.operations())
.unwrap()
.sort()
.unwrap()
.sorted();

for operation in document.operations() {
// For each operation in the db we insert a document, cumulatively adding the next operation
// each time. this should perform an "INSERT" first in the documents table, followed by 9 "UPDATES".
current_operations.push(operation.clone());
let document = DocumentBuilder::new(current_operations.clone())
.build()
for (operation_id, _, _) in ordered_operations {
let document = document_builder
.build_to_view_id(Some(operation_id.into()))
.unwrap();
let result = db.store.insert_document(&document).await;
assert!(result.is_ok());
Expand Down

0 comments on commit 9d54832

Please sign in to comment.