Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Diametric is a library for building schemas, queries, and transactions for Datomic from Ruby objects.
Ruby Java

This branch is 1 commit ahead, 72 commits behind relevance:master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
bin
ext/diametric
lib
samples
script
spec
.gitignore
.travis.yml
CHANGELOG.md
Gemfile
Guardfile
Jarfile
LICENSE.txt
README.md
Rakefile
datomic_version.yml
diametric-java.gemspec
diametric.gemspec

README.md

Build Status

Diametric

Diametric is ActiveRecord style wrapper for Datomic, and a library for building schemas, queries, and transactions for Datomic from Ruby objects. It is also used to map Ruby objects as entities into a Datomic database.

Diametric supports both CRuby and JRuby. When you are on CRuby, you can use Datomic's REST service. While you are creating data and making queries, Diametric is poking around Datomic's REST API. When you are on JRuby, you can use Datomic's REST and Peer services. If you are using Peer service, Diametric is poking Datomic's clojure API and some clojure functions.

For Rubyists who are familiar with object oriented programming, Diametric converts:

  • Ruby model definition to Datomic schema
  • Creating, saving and updating model data to Datomic transaction data
  • Ruby query to Datomic's datalog query

The biggest difference between REST and Peer services is available database types. REST service can use memory database only, which means you can't save data in any file. While Peer service can use memory and free version of transactor, which means you can save data in a file through Datomic's transactor. Other than that, Peer service has excellent API to use Datomic's various features.

Documents

Other than highlights below, there are documents on Wiki.

This document shows how to use Diametric with simple examples.

Installation

gem install diametric

Diametric is a multi-platoform gem. On CRuby, above installs CRuby version of gem. On JRuby, it is JRuby version of gem with Java extension.

[note] When you install diametric on JRuby, you'll see a message, "Building native extensions. This could take a while...." Although Diametric doesn't rely on C library, it depends on a several Java libraries. While installing gem, diametric downloads thoes jar archives including dependencies on your local maven repo. The message shows up because of this.

Preparation for CRuby

On CRuby, you need to start Datomic's REST server.

Diametric has a command to start REST server. Type datomic-rest -p port -a db_alias -u uri, for example

datomc-rest -p 9000 -a free -u datomic:mem://

When you run the command at the very first time, it takes long to start running. This is because Diametric downloads datomic if it doesn't exist in Diametric's directory tree. If it is the second time or later, the REST server starts quickly.

To learn more about the options in the above command, please go to http://docs.datomic.com/rest.html.

Once the REST server starts running, go to localhost:9000 on your browser. You can see Datomic's REST service is running.

Alternatively, you can download Datomic archive from http://downloads.datomic.com/free.htm, and start REST server using Datomic command, script/datomic-rest -p 9000 free datomic:mem://

Preparation for JRuby

You have 2 choices on JRuby, Peer and REST service. Peer service works on the same JVM as JRuby and is just a Java library for JRuby.

When you choose REST service, follow Preparation for CRuby section.

When you choose Peer service, you don't need to prepare anything. You even don't need to start Datomic. Diametric does everything for you. What you need to do is just coding in Ruby using Diametric API.

Although Diametric API makes coding very easy, you still have a freedom to hit Datomic's Java API directly.

Typical coding steps

Typical Diametric coding takes steps below:

  1. Connect to Datomic
  2. Define entities
  3. Create schema on Datomic from entity definitions
  4. Create entities and save it on Datomic
  5. Make a query to Datomic

Connect to the Datomic REST Service

To establish connection to Datomic REST Service, you can do as in below:

require 'diametric'
Diametric::Persistence.establish_base_connection({:uri => 'http://localhost:9000', :storage => 'free', :database => 'sample'})

Optionally, you can connect using datomic-clinet gem:

require 'datomic/client'

@datomic_uri = 'http://localhost:9000'
@storage = 'free'
@dbname = "sample"
@client = Datomic::Client.new @datomic_uri, @storage
@client.create_database(@dbname)

Each parameter should be consistent to the ones used to start REST service.

Connect to the Datomic Peer service (JRuby only)

require 'diametric'
Diametric::Persistence.establish_base_connection({:uri=>'datomic:mem://sample'})

Or in a peer service specific way,

datomic_uri = "datomic:mem://sample-#{SecureRandom.uuid}"
@conn = Diametric::Persistence::Peer.connect(datomic_uri)

Define an entity

While a relational databse inserts a record, Diametric saves Entity. To save data by Diametric, you need to define Entity first. Defining entities loosely corresponds to defining database tables.

Below is an example of Entity definition for Peer service. Be aware, you should include Diametric::Entity and either one of Diametric::Persistence::REST or Diametric::Persistence::Peerin your Entity definition.

require 'diametric'

class Person
  include Diametric::Entity
  include Diametric::Persistence::Peer

  attribute :name, String, index: true
  attribute :birthday, DateTime
  attribute :awesomeness, Boolean, doc: "Is this person awesome?"
end

The attribute definition is consists of:

  • attribute name (required)
  • attribute type (required)
  • attribute options (optional)

This is a Diametric way of converting Datomic schema to Ruby model definition.

The attribute name goes to :db/ident and the value will be :<class name>/<attribute name>. This is interpreted as :<namespace>/<name> on Datomic.

Currently, Diametric supports following data types:

Diametric Datomic
String :db.type/string
Symbol :db.type/keyword
Boolean :db.type/boolean
Integer :db.type/long
Float :db.type/double
Double :db.type/double
BigDecimal :db.type/bigdec
Ref :db.type/ref
DateTime :db.type/instant
UUID :db.type/uuid

In addition, Diametric supports Enum type.

Avaialbe options are:

Diametric Datomic
:cardinality => :one (default) :db.cardinality/one
:cardinality => :many :db.cardinality/many
:doc => "some document here" :db/doc
:unique => :identity :db.unique/identity
:unique => :value :db.unique/value
:index => true :db/index
:fulltext => true :db/fulltext

Create a schema

Creating schema on Datomic is similar to a migration on Ruby on Rails. However, schema creation can be done as a part of a program. For Datomic, the difference between schema and entity is quite small. Like saving entities, schemas are saved in Datomic.

Creating schema is done by calling create_schema method of the entity.

Person.create_schema.get

Person.create_schema (not followed by get) also creates a schema, but it is a sort of piling up on the queue and not performed immediately. If get is not specified, schema creation is done by at some point implicitly. Adding get requests Datomic to create schema now. This is good to know whether the entity definition is correct or not. If some attribute definition has a problem, error will be raised.

Create, save and update an entity

Once the entity is defined, and schema is created on Datomic, you can create an entity instance like you create a new object in Ruby. Below is an example:

person = Person.new
person.name = "Sky"
person.birthday = DateTime.parse('2005-01-01')
person.awesomeness = true

puts "dbid (before save): #{person.dbid}"

person.save

puts "dbid (after save): #{person.dbid}"

Above prints:

dbid (before save): 
dbid (after save): 17592186045418

The dbid is equivalent to id of ordinary ActiveModel objects. Before save, person object doesn't have dbid value. But after save, it will have an auto-generated value as dbid.

Alternatively, you can create an entity in one line:

Person.new(name: "Sky", birthday: DateTime.parse('2005-01-01'), awesomeness: true).save

To update attribue values of the instance, use update_attributes:

person.update_attributes(name: "Sky Jr.", awesomeness: false)

Make queries

Queries are done by Diametric::Query instance. A basic usage is:

  1. Create Diametric::Query instance with a required argument.
  2. Call methods of Query instance. This triggers a query to Datomic.
  3. Iterate the query result and do something to get data.

Diametric::Query has three arguments in constructor:

Diametric::Query.new(entity_class, db_or_conn=nil, resolve=false)

The second and the third options work only on Peer service.

On REST service, the query results are array of entity objects. On Peer service, the result data type depends on the constructor arguments. In default setting, the query result is a Set of Arrays. Each array has entity id (future Daimetric version will have a couple of attributes depends on the query). This data structure is exactly the same as what Datomic returns. Also, to avoid overhead comes from converting to Ruby's Set and Arrays, Diametric wraps in Diametric::Persistence::Set or Diametric::Persistence::Collection. (Diametric's wrappers are not a perfect Ruby Array nor Ruby Set in this version. Those will be improved in future versions.)

The reason Diametric doesn't create entity instances by defualt is to save memory. When dealing with millions or billions of data, saving memory is key to run faster, and, more importantly, to avoid OutOfMemoryError. In terms of saving memory, Diametric returns minimum by default.

If you don't have a lot of data and want to get instances directly from query results, set true to the third option, resolve.

When resolve option is set to true, the query result will be an Array of entity instances. You should be careful to set this option to true. When it is true, Diametric tries to create instances recursively following associations. If friends or friends of friends includs self, you'll likely to get StackOverflowError.

To resolve entity from a dbid, use Entity's reify method. The reify method creates an instance from the dbid.

The second option, db_or_conn is to specify the database state at some time in the past. If it is not specified, Diametric uses the default database state, which means the latest state.

find all

Query example1:

# create test data
Person.new(name: "Sun", birthday: DateTime.parse('2005-01-01'), awesomeness: true).save
Person.new(name: "Cloud", birthday: DateTime.parse('1980-02-12'), awesomeness: false).save
Person.new(name: "Breeze", birthday: DateTime.parse('1911-03-23'), awesomeness: true).save
Person.new(name: "Sleet", birthday: DateTime.parse('2010-04-30'), awesomeness: false).save
Person.new(name: "Rain", birthday: DateTime.parse('2005-05-05'), awesomeness: true).save

# make a query
query = Diametric::Query.new(Person)
query.each do |ary|
  person = Person.reify(ary.first)
  puts "name: #{person.name}, dbid: #{person.dbid}, birthday: #{person.birthday}, awesomeness: #{person.awesomeness}"
end

Above prints:

name: Rain, dbid: 17592186045429, birthday: 2005-05-04 20:00:00 -0400, awesomeness: true
name: Breeze, dbid: 17592186045425, birthday: 1911-03-22 19:00:00 -0500, awesomeness: true
name: Sleet, dbid: 17592186045427, birthday: 2010-04-29 20:00:00 -0400, awesomeness: false
name: Sun, dbid: 17592186045421, birthday: 2004-12-31 19:00:00 -0500, awesomeness: true
name: Cloud, dbid: 17592186045423, birthday: 1980-02-11 19:00:00 -0500, awesomeness: false

Query example2 (with resolve to true):

query = Diametric::Query.new(Person, nil, true)
query.each do |entity|
  puts "name: #{entity.name}, dbid: #{entity.dbid}, birthday: #{entity.birthday}, awesomeness: #{entity.awesomeness}"
end

The example2 prints exactly the same as the example1.

where

To narrow down the query results, you can use where and filter methods.

Query example3:

query = Diametric::Query.new(Person, nil, true).where(awesomeness: true)
query.each do |entity|
  puts "name: #{entity.name}, dbid: #{entity.dbid}, birthday: #{entity.birthday}, awesomeness: #{entity.awesomeness}"
end

Above prints:

name: Sun, dbid: 17592186045421, birthday: 2004-12-31 19:00:00 -0500, awesomeness: true
name: Rain, dbid: 17592186045429, birthday: 2005-05-04 20:00:00 -0400, awesomeness: true
name: Breeze, dbid: 17592186045425, birthday: 1911-03-22 19:00:00 -0500, awesomeness: true

filter

Query example4:

query = Diametric::Query.new(Person, nil, true).filter(:>, :birthday, DateTime.parse('2004-12-31'))
query.each do |entity|
  puts "name: #{entity.name}, dbid: #{entity.dbid}, birthday: #{entity.birthday}, awesomeness: #{entity.awesomeness}"
end

The results are:

name: Sun, dbid: 17592186045418, birthday: 2004-12-31 19:00:00 -0500, awesomeness: true
name: Sleet, dbid: 17592186045424, birthday: 2010-04-29 20:00:00 -0400, awesomeness: false
name: Rain, dbid: 17592186045426, birthday: 2005-05-04 20:00:00 -0400, awesomeness: true

Currently, filter supports simple Clojure comparisons. For example:

(< attribute value)
(> attribute value)
(= attribute value)
...

The first argument of filter is mapped to the predicate of the clojure function. The second argument of filter is always an attrbute name, which will be replaced by attribute values of entities. The third argument of the filter is the value to be compared.

chaining

Ruby's method chaining can be used for where and filter methods.

Query example5:

query = Diametric::Query.new(Person, nil, true).where(awesomeness: true).filter(:>, :birthday, DateTime.parse('2004-12-31'))
query.all.each do |p|
  puts "name: #{p.name}, dbid: #{p.dbid}, birthday: #{p.birthday}, awesomeness: #{p.awesomeness}"
end

The query finds entities whose awesomenesses are true and whose birthdays are after December 31, 2004. As expected, it prints:

name: Sun, dbid: 17592186045418, birthday: 2004-12-31 19:00:00 -0500, awesomeness: true
name: Rain, dbid: 17592186045426, birthday: 2005-05-04 20:00:00 -0400, awesomeness: true

short cut methods

So far, the query examples use Diametric::Query.new(...). Other than that, Diametric supports consice short cut query methods. The examples above can be rewritten below. In this case, resolve option is set to true by default.

#query = Diametric::Query.new(Person)
query = Person.all

#query = Diametric::Query.new(Person, nil, true).where(awesomeness: true)
query = Person.where(awesomeness: true)

#query = Diametric::Query.new(Person, nil, true).filter(:>, :birthday, DateTime.parse('2004-12-31'))
query = Person.filter(:>, :birthday, DateTime.parse('2004-12-31'))

make query to the past (Peer only)

Datomic has the idea of time. It allows database state to role back at some point in the past. The example below makes the query to the current and secounds (milliseconds?) before of database.

Query example6:

# prints current awesomeness values
Diametric::Query.new(Person, nil, true).all.collect(&:awesomeness)
 => [true, true, false, true, false]

# saves the time. this will be the past
past = Time.now
 => 2013-11-09 20:17:59 -0500

# updates awesomeness false to true
Diametric::Query.new(Person, nil, true).where(awesomeness: false).each do |person|
  person.update_attributes(awesomeness: true)
end
 => [[17592186045420], [17592186045424]]

# prints updated awesomeness values
Diametric::Query.new(Person, nil, true).all.collect(&:awesomeness)
 => [true, true, true, true, true]

# pulls out the past database
past_db = @conn.db.as_of(past)
 => #<Diametric::Persistence::Database:0x49fe0bcd>

# makes a query to past database
Diametric::Query.new(Person, past_db, true).all.collect(&:awesomeness)
 => [true, true, false, true, false]

# query results shows the database before "past"

using datomic's query string (Peer only)

Currently, Diametric's query can do only a part of Datomic can do. When you want a more complicated query or customized query, you can use Diametric::Persistence::Peer.q method. For example:

result = Diametric::Persistence::Peer.q("[:find ?name :where [?person :person/name ?name]]", @conn.db)

You'll get the results below:

[["Rain"], ["Sleet"], ["Sun"], ["Cloud"], ["Breeze"]]

This result data type is a Set of Arrays, which is the same as the default setting.

Below is another example with a value argument. This is what Diametric's filter query does.

result = Diametric::Persistence::Peer.q("[:find ?birthday :in $ [?value] :where [?e :person/birthday ?birthday] [(< ?birthday ?value)]]", @conn.db, DateTime.parse('2002-01-01'))

The result is:

[[#inst "1980-02-12T00:00:00.000-00:00"], [#inst "1911-03-23T00:00:00.000-00:00"]]

This is a Set of Arrays whose elements are Datomic's Date expression. However, Diametric wrapper converts to Ruby object when it is used.

result.each do |ary|
  puts ary.first.strftime("%m/%d/%Y")
end

returns:

02/11/1980
03/22/1911

Delete entities

To delete an entity, use destroy method:

query = Person.where(:name => "Sleet")
query.all.each {|p| p.destroy }

Validation

ActiveModel's validation are included by default. All you need to do is start using them!

require 'diametric'

class Person
  include Diametric::Entity
  include Diametric::Persistence

  attribute :name, String, :index => true

  validates :name, :length => {:minimum => 5}
end
Person.create_schema

This model validates the length of the name. If the name has less than 5 letters, the input is invalid. For example:

jeff = Person.new(:name => "Geoffrey")
jeff.valid?
# => true

jeff.name = "Jeff"
jeff.valid?
# => false

Similarly to ActiveRecord, models cannot save until they are valid:

jeff.name = "Jeff"
jeff.save
# => false

jeff.name = "Goeffrey"
jeff.save 
# => #<... transaction results object ...>

Cardinality

When you want to define one-to-many attributes, you should add :cardinality => :many to an attribute definition.

class Profile
  include Diametric::Entity
  include Diametric::Persistence::Peer

  attribute :name, String, :index => true
  attribute :likes, String, :cardinality => :many
end
Profile.create_schema.get

The attribute with :cardinality => :many accepts either Array or Set. Usage will be:

Profile.new(name: "Breeze", likes: ["chocolate", "biking", "afternoon"]).save
Profile.new(name: "Sun", likes: ["banana", "running", "morning"]).save
Profile.new(name: "Rain", likes: (Set.new ["pumpkin pie", "video game", "night"])).save

Datomic saves values in a Set object when the attribute has :cardinality => :many definition. For example:

Profile.where(:name => "Breeze").each {|e| puts e.likes}

returns #<Set:0x007fbb66294928>. So, if the following gets run:

Diametric::Query.new(Profile).each do |ary|
  profile = Profile.reify(ary.first)
  puts "#{profile.name} likes #{profile.likes.inspect}"
end

Above prints:

Rain likes #<Set: {"pumpkin pie", "video game", "night"}>
Sun likes #<Set: {"morning", "banana", "running"}>
Breeze likes #<Set: {"chocolate", "afternoon", "biking"}>

The order is not guaranteed.

Association (Peer only)

On Datomic, association is fairly easy even though it is one to many or many to many. Association is defined by Ref type. Just assigning a saved entity instance to Ref type attribute makes association.

The entity definition looks like below:

class Somebody
  include Diametric::Entity
  include Diametric::Persistence::Peer

  attribute :name, String
  attribute :mom, Ref, :cardinality => :one
  attribute :dad, Ref, :cardinality => :one
  attribute :kids, Ref, :cardinality => :many
end

This entity has one to one and one to many associations. Below is an example of creating an entity instance and making queries.

# creates mom and dad
mom = Somebody.new(name: "Snow White")
 => #<Somebody:0x3292eff7 @changed_attributes={"name"=>nil}, @name="Snow White">
mom.save
 => {:db-before datomic.db.Db@f89fe443, :db-after datomic.db.Db@efb79a88, :tx-data [#Datum{:e 13194139534333 :a 50 :v #inst "2013-11-10T03:35:06.889-00:00" :tx 13194139534333 :added true} #Datum{:e 17592186045438 :a 67 :v "Snow White" :tx 13194139534333 :added true}], :tempids {-9223350046623220305 17592186045438}}
dad = Somebody.new(name: "Prince Brave")
 => #<Somebody:0x1425e531 @changed_attributes={"name"=>nil}, @name="Prince Brave">
dad.save
 => {:db-before datomic.db.Db@efb79a88, :db-after datomic.db.Db@3bddfe3, :tx-data [#Datum{:e 13194139534335 :a 50 :v #inst "2013-11-10T03:35:09.405-00:00" :tx 13194139534335 :added true} #Datum{:e 17592186045440 :a 67 :v "Prince Brave" :tx 13194139534335 :added true}], :tempids {-9223350046623220306 17592186045440}}

# creates sombody who has mom and dad
Somebody.new(name: "Alice Wonderland", mom: mom, dad: dad).save
 => {:db-before datomic.db.Db@3bddfe3, :db-after datomic.db.Db@df977d06, :tx-data [#Datum{:e 13194139534337 :a 50 :v #inst "2013-11-10T03:35:39.562-00:00" :tx 13194139534337 :added true} #Datum{:e 17592186045442 :a 68 :v 17592186045438 :tx 13194139534337 :added true} #Datum{:e 17592186045442 :a 69 :v 17592186045440 :tx 13194139534337 :added true} #Datum{:e 17592186045442 :a 67 :v "Alice Wonderland" :tx 13194139534337 :added true}], :tempids {-9223350046623220307 17592186045442}}

# makes a query whose name is "Alice Wonderland"
me = Diametric::Query.new(Somebody, @conn, true).where(name: "Alice Wonderland").first
 => #<Somebody:0x5f369fc6 @changed_attributes={"dad"=>nil, "mom"=>nil, "name"=>nil}, @dbid=17592186045442, @name="Alice Wonderland", @dad=#<Somebody:0x75de7009 @changed_attributes={"name"=>nil}, @dbid=17592186045440, @name="Prince Brave">, @mom=#<Somebody:0x7c840fe3 @changed_attributes={"name"=>nil}, @dbid=17592186045438, @name="Snow White">>

# creates another two others who have mom as me
mario = Somebody.new(name: "Mario", mom: me)
 => #<Somebody:0x6ad3fbe4 @changed_attributes={"name"=>nil, "mom"=>nil}, @name="Mario", @mom=#<Somebody:0x5f369fc6 @changed_attributes={"dad"=>nil, "mom"=>nil, "name"=>nil}, @dbid=17592186045442, @name="Alice Wonderland", @dad=#<Somebody:0x75de7009 @changed_attributes={"name"=>nil}, @dbid=17592186045440, @name="Prince Brave">, @mom=#<Somebody:0x7c840fe3 @changed_attributes={"name"=>nil}, @dbid=17592186045438, @name="Snow White">>>
mario.save
 => {:db-before datomic.db.Db@df977d06, :db-after datomic.db.Db@79b5b85f, :tx-data [#Datum{:e 13194139534339 :a 50 :v #inst "2013-11-10T03:36:37.567-00:00" :tx 13194139534339 :added true} #Datum{:e 17592186045444 :a 68 :v 17592186045445 :tx 13194139534339 :added true} #Datum{:e 17592186045444 :a 67 :v "Mario" :tx 13194139534339 :added true} #Datum{:e 17592186045445 :a 68 :v 17592186045447 :tx 13194139534339 :added true} #Datum{:e 17592186045445 :a 69 :v 17592186045446 :tx 13194139534339 :added true} #Datum{:e 17592186045445 :a 67 :v "Alice Wonderland" :tx 13194139534339 :added true} #Datum{:e 17592186045446 :a 67 :v "Prince Brave" :tx 13194139534339 :added true} #Datum{:e 17592186045447 :a 67 :v "Snow White" :tx 13194139534339 :added true}], :tempids {-9223350046623220311 17592186045447, -9223350046623220310 17592186045446, -9223350046623220309 17592186045445, -9223350046623220308 17592186045444}}
luigi = Somebody.new(name: "Luigi", mom: me)
 => #<Somebody:0x4ebed2b3 @changed_attributes={"name"=>nil, "mom"=>nil}, @name="Luigi", @mom=#<Somebody:0x5f369fc6 @temp_ref=-1013, @changed_attributes={}, @dbid=17592186045445, @name="Alice Wonderland", @dad=#<Somebody:0x75de7009 @temp_ref=-1014, @changed_attributes={}, @dbid=17592186045446, @name="Prince Brave", @previously_changed={"name"=>[nil, "Prince Brave"]}>, @previously_changed={"dad"=>[nil, #<Somebody:0x75de7009 @temp_ref=-1014, @changed_attributes={}, @dbid=17592186045446, @name="Prince Brave", @previously_changed={"name"=>[nil, "Prince Brave"]}>], "mom"=>[nil, #<Somebody:0x7c840fe3 @temp_ref=-1015, @changed_attributes={}, @dbid=17592186045447, @name="Snow White", @previously_changed={"name"=>[nil, "Snow White"]}>], "name"=>[nil, "Alice Wonderland"]}, @mom=#<Somebody:0x7c840fe3 @temp_ref=-1015, @changed_attributes={}, @dbid=17592186045447, @name="Snow White", @previously_changed={"name"=>[nil, "Snow White"]}>>>
luigi.save
 => {:db-before datomic.db.Db@79b5b85f, :db-after datomic.db.Db@9a8e7dab, :tx-data [#Datum{:e 13194139534344 :a 50 :v #inst "2013-11-10T03:36:37.649-00:00" :tx 13194139534344 :added true} #Datum{:e 17592186045449 :a 68 :v 17592186045445 :tx 13194139534344 :added true} #Datum{:e 17592186045449 :a 67 :v "Luigi" :tx 13194139534344 :added true}], :tempids {-9223350046623220312 17592186045449}}
me.update_attributes(kids: [mario, luigi])
 => {:db-before datomic.db.Db@9a8e7dab, :db-after datomic.db.Db@7507454d, :tx-data [#Datum{:e 13194139534346 :a 50 :v #inst "2013-11-10T03:36:38.821-00:00" :tx 13194139534346 :added true} #Datum{:e 17592186045445 :a 70 :v 17592186045444 :tx 13194139534346 :added true} #Datum{:e 17592186045445 :a 70 :v 17592186045449 :tx 13194139534346 :added true}], :tempids {}}

# again, makes a query whose name is "Alice Wonderland"
me = Diametric::Query.new(Somebody, @conn, true).where(name: "Alice Wonderland").first
 => #<Somebody:0x1eb6037d @mom=#<Somebody:0x335b3d6 @dbid=17592186045427, @changed_attributes={"name"=>nil}, @name="Snow White">, @dad=#<Somebody:0x38848217 @dbid=17592186045426, @changed_attributes={"name"=>nil}, @name="Prince Brave">, @dbid=17592186045425, @changed_attributes={"kids"=>nil, "dad"=>nil, "mom"=>nil, "name"=>nil}, @kids=#<Set: {#<Somebody:0x3c743d40 @mom=#<Java::DatomicQuery::EntityMap:0x77a9ac36>, @dbid=17592186045424, @changed_attributes={"mom"=>nil, "name"=>nil}, @name="Mario">, #<Somebody:0x2444c3df @mom=#<Java::DatomicQuery::EntityMap:0x5aac6f9f>, @dbid=17592186045429, @changed_attributes={"mom"=>nil, "name"=>nil}, @name="Luigi">}>, @name="Alice Wonderland">

# looks kids data
me.kids.collect(&:name)
 => ["Mario", "Luigi"]

# retieve mom's instance from kids
Somebody.reify(me.kids.first.mom).name
 => "Alice Wonderland"

Thanks

Development of Diametric was sponsored by Relevance. They are the best Clojure shop around and one of the best Ruby shops. I highly recommend them for help with your corporate projects.

Special thanks to Mongoid for writing some solid ORM code that was liberally borrowed from to add Rails support to Diametric.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

License

This project uses the BSD License.

Copyright (c) 2012, Clinton Dreisbach & Relevance Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Something went wrong with that request. Please try again.