Ruby Adventures with ArcadeDB 2
Tags: ruby, nosql, orm, arcadedb
Published: 2023-12-11 17:27:20

**note:** Clone the git respiratory  https://github.com/topofocus/dev-adventures to play with the data. **:end-note**

Part One:  [Ruby Adventures with ArcadeDB](https://dev.to/topofocus/ruby-adventures-with-aradedb-45em)

## Some Basics

ActiveRecord is the standard abstraction layer for databases in ruby. It translates ruby code to SQL, optimizes queries and is compatible to many low level database drivers. Its promise: You don't have to know anything about databases, can use every relational database – everywhere. Just stick to ruby code. 

Relational databases are a popular way to organize structured data. But data sets aren't always neatly organized. Sometimes, the data is forced into a mold that doesn't fit just because the tool requires it.

[ArcadeDB](https://arcadedb.com) is a modern multi model database. Its promise is analogous  to ActiveRecord: Whatever the structure of your data is, the database offers an effective host. The [ruby-interface to ArcadeDB](https://github.com/topofocus/arcadedb) aims to combine the 
user friendlines of ActiveRecord and the flexibility of ArcadeDB. 

## The Power of RID

Relational Databases organize data in tables. Very often, a column is called `id` and carries the index.  [ArcadeDB](https://arcadedb.com) too has tables. They're called `types` and carry object-oriented behaviors.  In addition the local data-structure can always inspected via `RID`'s. 
This is happening:

> `select from #1:0`  
> {:@rid=>"#449:0", :@type=>"human", :@cat=>"v", :name=>"hugo }   
 
The `RID` exposes itself as `human`-type, its a vertex (@cat: "v") and has a property `name`. 

The [ruby-interface](https://github.com/topofocus/arcadedb)  connects the @type to an appropriate `Model class`, assigns the database properties to corresponding model-attributes and exposes the data to customized model-methods. 

> Its much simpler to access pieces of data from ArcadeDB then from any relational database. [ArcadeDB](https://arcadedb.com) is a native object database, no abstraction is needed to fit the data representation in the database to the data-model in your ruby program. 

## Exploring the Structure

The `Account`-Type is a vertex. This implies, it might have edges, that connect it to other vertices. This is explored with the `nodes`-method

```ruby
> first_human =  DB.get 1,0      # select from #1:0
> [<human[#1:0]:{0->}{->1}, name: hugo>] 
> first_account.nodes              # select both() from #1:0
> [<human[#2:0]:{1->}{->0}, name: berta>] 
``` 
A connected `Account`-record is retrieved!

## Bidirectional connections – with attributes

Lets design a simple relation


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w1jdkresj07aire4t65j.jpg)

```ruby
> first_account.assign via: IsMarriedTo
                        to: second_account
                      date: Date.new( 1955, 12, 21)
                      town: "Dublin"
```

If -- by convention -- outgoing connections belong to the male and incoming one to the female, a query of Human's which got `out`-connections of type `IsMarriedTo`, loads a list of married man. Compare that to efforts needed with relational databases. 

Its possible to use the `node`-method here, too. 
But ArcadeDB offers a versatile alternative.

```
Arcade::Match.new( type: Human, as: :h)
             .out( IsMarriedTo )
             .execute { "h.rid, h.name" }
```
`Arcade::Match` is a simple wrapper of the [Match Statement](https://docs.arcadedb.com/#SQL-Match), which »queries the database in a declarative manner, using pattern matching (inspired by Cypher)«.
The compiled match statement looks like this:
```
MATCH { type: human, as: h }
      .out( 'is_married_to')
RETURN h.rid, h.name
```
and returns an array with all rid's and names of the suspects.  
The query is easily extended to only marriages in the last decade of the 20th century:
```ruby
Arcade::Match.new( type: Human, as: :h)
             .out( IsMarriedTo , where: { date: 1990 .. 2000})
             .execute { "h.rid, h.name" }
=> MATCH { type: human, as: h }
      .out( 'is_married_to', where: ( date between 1990 and 2000) )
RETURN h.rid, h.name
```
The properties on the bidirectional connection(edge) are essential to this query. 

ArcadeDB is not only a object-database regarding to the data-storage but to the connections between the data itself. 
If a couple divorces, the `IsMarriedTo`connection is replaced by a `IsDivorced`-edge. Both edge-classes are childs of `IsFriend` Edges. 

Until now, this example lacks realism, as same-sex marriages are not allowed (Only man have `out`-connections). This can easily be addressed via inheritance.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4of0kr1arbs24v8d7my1.jpg)

The final setup of the database looks as following
```ruby
DB = Arcade::Init.db  # Reference to the database
[Male, Female, IsMarriedTo, IsDivorced].each { |y| y.create_type  }  
# Inheritance is respected, `Human` and `IsFriend` are allocated implicitly

hugo, paul, jochen = ['Hugo', 'Paul', 'Jochen'].map do | man |
   Male.insert name: man
end
berta, hilde, maria = ['Berta', 'Hilde', 'Maria'].map do | woman |
   Female.insert name: woman
end
hugo.assign via: IsMarriedTo, town: 'Duesseldorf', date: Date.new(1956,12,1), to: berta
paul.assign via: IsDivorced, date: Date.new(1959,4,5), to: hilde
maria.assign via: IsFriend, to: [ jochen, hilde, hugo ]
```
Verify the data structure 
```ruby
Human.all
=> [<male[#66:0]:{1->}{->1}, name: Hugo>,                 
    <male[#69:0]:{0->}{->1}, name: Paul>,                 
    <male[#72:0]:{1->}{->0}, name: Jochen>,
  <female[#90:0]:{1->}{->0}, name: Berta>,
  <female[#93:0]:{2->}{->0}, name: Hilde>,
  <female[#96:0]:{0->}{->3}, name: Maria>]

Female.find( name: 'Maria').out
=>                                                                 
[  <male[#66:0]:{1->}{->1}, name: Hugo>,                              
 <female[#93:0]:{2->}{->0}, name: Hilde>,                           
   <male[#72:0]:{1->}{->0}, name: Jochen>]
   
# query for divorced couples
Arcade::Match.new( type: Human, as: m)
             .out(IsDivorced)
             .node( as: :f)
             .execute { "m.name, f.name" }

INFO->Q: MATCH { type: human, as: m }
                .out('is_divorced')
               { as: f } 
         RETURN m.name, f.name 
 =>                                                                                                          
[{:m.name =>"Paul", :f.name=>"Hilde"}]  
```
( to be continued ... )