Using examples from SQL vs. Slick examples:
// Slick
people.result
In molecule we would declare each attribute we are interested in also to infer the exact return type
// Molecule
Person.name.age.get
people.map(p => (p.age, p.name ++ " (" ++ p.id.asColumnOf[String] ++ ")")).result
With Molecule we would concatenate name
and id
with the returned result set:
Person.age.name.e.get.map(_.map { case (age, name, id) => (age, s"$name ($id)" } )
people.filter(p => p.age >= 18 && p.name === "C. Vogt").result
Molecule filter values by applying a required value to an attribute or supply a value to compare against (>=(18)
):
Person.age.>=(18).name("C. Vogt").get
(Again we would define which attribute values we want to return)
people.sortBy(p => (p.age.asc, p.name)).result
Ordering is applied on the result set in the application code:
Person.age.name.get.sortBy(_._1)
people.map(_.age).max.result
Aggregate functions like max
are all applied as a keyword value to an attribute.
Person.age(max).get
// or get a range of top values
Person.age(max(3)).get
We can aggregate values also with the counterpart min
or get a random value with rand
. Or perform aggregate calculations with count
, countDistinct
, sum
, avg
, median
, variance
and stddev
which are all built functions in Datomic.
people.groupBy(p => p.addressId)
.map{ case (addressId, group) => (addressId, group.map(_.age).avg) }
.list
Molecule automatically group by attributes not having an aggregate expression. In this case the query will group by address
and calculate the average age
for persons living there.
Person.address.age(avg).get
people.groupBy(p => p.addressId)
.map{ case (addressId, group) => (addressId, group.map(_.age).avg) }
.filter{ case (addressId, avgAge) => avgAge > 50 }
.map(_._1)
.result
Person.address.age(avg).get.map(_.filter(_._2 > 50))
people.flatMap(p =>
addresses.filter(a => p.addressId === a.id)
.map(a => (p.name, a.city))
).result
// or equivalent for-expression:
(for(p <- people;
a <- addresses if p.addressId === a.id
) yield (p.name, a.city)
).result
Person.name.Address.city.get
(people join addresses on (_.addressId === _.id))
.map{ case (p, a) => (p.name, a.city) }.result
Person.name.Address.city.get
(addresses joinLeft people on (_.id === _.addressId))
.map{ case (a, p) => (p.map(_.name), a.city) }.result
// Add `$` to attribute name to get optional values
Person.name$.Address.city.get
val address_ids = addresses.filter(_.city === "New York City").map(_.id)
people.filter(_.id in address_ids).result // <- run as one query
Person.age.name.Address.city_("New York City").get
people.map(p => (p.name, p.age, p.addressId))
.insert(("M Odersky",12345,1))
Person.name("M Odersky").age(12345).address(1).save
people.filter(_.name === "M Odersky")
.map(p => (p.name,p.age))
.update(("M. Odersky",54321))
for {
// Find entity id with meta Molecule attribute `e`
oderskyId <- Person.e.name_("M Odersky").get.map(_.head)
_ <- Person(oderskyId).name("M. Odersky").age(54321).update
} yield ()
people.filter(p => p.name === "M. Odersky")
.delete
Person.e.name_("M. Odersky").get.head.retract
people.map(p =>
Case
If(p.addressId === 1) Then "A"
If(p.addressId === 2) Then "B"
).list
Person.address(1 or 2).get.map(_.map {
case 1 => "A"
case 2 => "B"
})