Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

fn update

The update fn takes a mutable reference to an Entity and persists any new events that have been added to it. It will also UPDATE the row in the index table with the latest values derived from the entities attributes. It returns the number of events that were persisted.

In the code below we have a name column in the index table that needs to be kept in sync with the entity's state.

extern crate es_entity;
extern crate sqlx;
extern crate serde;
extern crate tokio;
extern crate anyhow;
use serde::{Deserialize, Serialize};
es_entity::entity_id! { UserId }
#[derive(EsEvent, Debug, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[es_event(id = "UserId")]
pub enum UserEvent {
    Initialized { id: UserId, name: String },
    NameUpdated { name: String },
}
impl IntoEvents<UserEvent> for NewUser {
    fn into_events(self) -> EntityEvents<UserEvent> {
        EntityEvents::init(
            self.id,
            [UserEvent::Initialized {
                id: self.id,
                name: self.name,
            }],
        )
    }
}
impl TryFromEvents<UserEvent> for User {
    fn try_from_events(mut events: EntityEvents<UserEvent>) -> Result<Self, EsEntityError> {
        let mut name = String::new();
        for event in events.iter_all() {
            match event {
                UserEvent::Initialized { name: n, .. } => name = n.clone(),
                UserEvent::NameUpdated { name: n } => name = n.clone(),
            }
        }
        Ok(User { id: events.id().clone(), name, events })
    }
}
pub struct NewUser {
    id: UserId,
    name: String
}
use es_entity::*;

#[derive(EsEntity)]
pub struct User {
    pub id: UserId,
    // The name attribute on the `Entity` must be accessible 
    // for updates of the `index` table.
    name: String,
    events: EntityEvents<UserEvent>,
}

impl User {
    pub fn change_name(&mut self, name: String) {
        self.events.push(UserEvent::NameUpdated { name: name.clone() });
        self.name = name;
    }
}

#[derive(EsRepo)]
#[es_repo(entity = "User", columns(name = "String"))]
pub struct Users {
    pool: sqlx::PgPool
}

async fn init_pool() -> anyhow::Result<sqlx::PgPool> {
    let pg_con = format!("postgres://user:password@localhost:5432/pg");
    Ok(sqlx::PgPool::connect(&pg_con).await?)
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let users = Users { pool: init_pool().await? };
    
    // First create a user
    let new_user = NewUser { id: UserId::new(), name: "Fred".to_string() };
    let mut user = users.create(new_user).await?;
    
    // Now update the user
    user.change_name("Frederick".to_string());
    
    // The `update` fn takes a mutable reference to an `Entity` and persists new events
    let n_events = users.update(&mut user).await?;
    assert_eq!(n_events, 1); // One NameUpdated event was persisted

    Ok(())
}

The update part of the update function looks somewhat equivalent to:

impl Users {
    pub async fn update(
        &self,
        entity: &mut User
    ) -> Result<usize, es_entity::EsRepoError> {
        // Check if there are any new events to persist
        if !entity.events().any_new() {
            return Ok(0);
        }

        let id = &entity.id;
        // The attribute specified in the `columns` option
        let name = &entity.name;

        sqlx::query!("UPDATE users SET name = $2 WHERE id = $1",
            id as &UserId,
            name as &String
        )
        .execute(self.pool())
        .await?;

        // persist new events
        // execute post_persist_hook
        // return number of events persisted
    }
}

The key thing to configure is how the columns of the index table get updated via the update option. The update(accessor = "<>") option modifies how the field is accessed on the Entity type.

extern crate es_entity;
extern crate sqlx;
extern crate serde;
fn main () {}
use serde::{Deserialize, Serialize};
es_entity::entity_id! { UserId }
#[derive(EsEvent, Debug, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[es_event(id = "UserId")]
pub enum UserEvent {
    Initialized { id: UserId, name: String },
}
impl IntoEvents<UserEvent> for NewUser {
    fn into_events(self) -> EntityEvents<UserEvent> {
        unimplemented!()
    }
}
impl TryFromEvents<UserEvent> for User {
    fn try_from_events(events: EntityEvents<UserEvent>) -> Result<Self, EsEntityError> {
        unimplemented!()
    }
}
pub struct NewUser { id: UserId, name: String }
use es_entity::*;

#[derive(EsEntity)]
pub struct User {
    pub id: UserId,
    name: String,
    events: EntityEvents<UserEvent>,
}

impl User {
    pub fn display_name(&self) -> String {
        format!("User: {}", self.name)
    }
}

#[derive(EsRepo)]
#[es_repo(
    entity = "User",
    columns(
        // Instead of using the `name` field on the `Entity` struct
        // the generated code will use: `entity.display_name()`
        // to populate the `name` column during updates.
        name(ty = "String", update(accessor = "display_name()")),
    )
)]
pub struct Users {
    pool: sqlx::PgPool
}

The update(persist = false) option prevents updating the column. This is useful for columns that should never change after creation.

extern crate es_entity;
extern crate sqlx;
extern crate serde;
fn main () {}
use serde::{Deserialize, Serialize};
es_entity::entity_id! { UserId }
#[derive(EsEvent, Debug, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[es_event(id = "UserId")]
pub enum UserEvent {
    Initialized { id: UserId, name: String },
}
impl IntoEvents<UserEvent> for NewUser {
    fn into_events(self) -> EntityEvents<UserEvent> {
        unimplemented!()
    }
}
impl TryFromEvents<UserEvent> for User {
    fn try_from_events(events: EntityEvents<UserEvent>) -> Result<Self, EsEntityError> {
        unimplemented!()
    }
}
use es_entity::*;

// Assume the name of a user is immutable.
pub struct NewUser { id: UserId, name: String }

#[derive(EsEntity)]
pub struct User {
    pub id: UserId,
    // Exposing the `name` attribute on the `Entity` is optional
    // as it does not need to be accessed during update.
    // name: String
    events: EntityEvents<UserEvent>,
}

#[derive(EsRepo)]
#[es_repo(
    entity = "User",
    columns(
        name(ty = "String", update(persist = false))
    )
)]
pub struct Users {
    pool: sqlx::PgPool
}

Note that if no columns need updating (all columns have update(persist = false)), the UPDATE query is skipped entirely for better performance.