Card-many relationships

Tests…

Cardinality-many relationships in Molecule are modelled with the many[<RefNamespace>] syntax:

object OrderDefinition {

  trait Order {
    val id    = oneString
    val items = many[LineItem].isComponent
  }

  trait LineItem {
    val qty     = oneInt
    val product = oneString
    val price   = oneDouble
  }
}

An Order can have multiple LineItems so we define a cardinality-many ref attribute items that points to the LineItem namespace.

Note how we also make LineItems a component with the isComponent option. That means that LineItems are owned by an Order and will get automatically retracted if the Order is retracted. Subsequent component-defined referenced entities will be recursively retracted too.

Now we can get an Order and its Line Items:

Order.id.Items.qty.product.price.get === List(
  ("order1", 3, "Milk", 12.00),
  ("order1", 2, "Coffee", 46.00),
  ("order2", 4, "Bread", 5.00)
)

The Order data is repeated for each line Item which is kind of redundant. We can avoid that with a “nested” Molecule instead:

Nested results

Tests…

We can nest the result with the Molecule syntax * indicating “with many”:

m(Order.id.Items * LineItem.qty.product.price).get === List(
  ("order1", List(
    (3, "Milk", 12.00), 
    (2, "Coffee", 46.00))),
  ("order2", List(
    (4, "Bread", 5.00)))
)

Now each Order has its own list of typed Line Item data and there is no Order redundancy.

This becomes more and more handy the deeper the hierarchy of data is. Molecule can nest data structures up to 10 levels deep!

Entity API

Tests…

We can get a similar - but un-typed - nested hierarchy of data with the Entity API by calling touch on an order id:

// Touch entity facts hierarchy recursively
orderId.touch === Map(
  ":db/id" -> 101L,
  ":order/id" -> "order1",
  ":order/items" -> List(
    Map(
      ":db/id" -> 102L, 
      ":lineItem/qty" -> 3, 
      ":lineItem/product" -> "Milk",
      ":lineItem/price" -> 12.0),
    Map(
      ":db/id" -> 103L, 
      ":lineItem/qty" -> 2, 
      ":lineItem/product" -> "Coffee",
      ":lineItem/price" -> 46.0)))

Next

Composites…