Chromia
Start Building

05 / 06

One-to-one relationship

In a one-to-one relationship, each record in one table matches exactly one record in another table. Consider a library system: every library has its own unique address, and every address belongs to exactly one library. Each library entry therefore maps to a single, specific address.

A traditional relational database models this with separate tables linked by foreign keys. Guaranteeing uniqueness and data integrity this way adds complexity. The following SQL shows a typical one-to-one setup:

sql
CREATE TABLE Address (    id INT PRIMARY KEY,    street VARCHAR(255),    city VARCHAR(255),    postalCode VARCHAR(10));CREATE TABLE Library (    id INT PRIMARY KEY,    name VARCHAR(255),    address_id INT,    FOREIGN KEY (address_id) REFERENCES Address(id));

The schema works as follows:

  • Address table: Stores the address details and uses a primary key ID to uniquely identify each address.
  • Library table: Stores the library details plus a foreign key, address_id, that points to the ID in the Address table. The UNIQUE constraint prevents an address from being linked to more than one library.

Rell removes the need to manage foreign keys and constraints by hand: you reference entities directly.

Start by creating a library_management directory inside /src. In it, add a module.rell file containing this code:

src/library_management/module.rell
module;

Next, add an entities.rell file to the /src/library_management directory with the following code:

src/library_management/entities.rell
entity library {    library_name: text;     // Name of the library    key address;    // One-to-one relationship with Address}entity address {    street: text;         // Street address    city: text;           // City name    postal_code: text;    // Postal code}

Now write an operation that adds a library together with its address. It creates both a Library and an Address record and connects them.

Add an operations.rell file to the /src/library_management directory with this code:

/src/library_management/operations.rell
operation create_library(name: text, street: text, city: text, postal_code: text) {    val library_address = create address(street, city, postal_code);    create library(name, library_address);}

The operation first builds an address instance from the supplied street, city, and postal code. It then creates a library from the supplied name and that address instance. Rell links the two records for you.

To make testing easier, define queries that return the stored records. Put this code in the queries.rell file:

src/library_management/queries.rell
query get_all_library() = library @* {} ( id = .rowid, .library_name, city = .address.city, street = .address.street, postal_code = .address.postal_code );query get_library_by_name(library_name: text) = library @ { library_name };

Testing

Add tests to confirm that the code behaves as expected. Rell supports writing tests in test modules.

Open src/test/my_library_test.rell and add a test. It creates a library with an address, then calls a query that returns every library in the database and checks how many there are:

src/test/my_library_test.rell
@test module;import library_management.{ create_library, get_all_library };function test_create_library() {    // Create a new library with an address    rell.test.tx()        .op(create_library("Biblioteca Apostolica Vaticana", "Città del Vaticano", "Vatican", "00120"))        .op(create_library("New York Public Library", "476 5th Ave", "New York", "10018"))        .run();    val all_libraries = get_all_library();    assert_equals(all_libraries.size(), 2);}

From the project folder, run this command in the terminal to execute the test:

shell
chr test

A passing test produces output like this:

shell
====================Running unit tests====================TEST: test.my_library_test:test_create_libraryOK: test.my_library_test:test_create_library (5.010s)------------------------------------------------------------TEST RESULTS:OK test.my_library_test:test_create_librarySUMMARY: 0 FAILED / 1 PASSED / 1 TOTAL (5.010s)***** OK *****

Creating new records

The example below uses the Chromia CLI to add library records. The next section queries those same records.

You need a local Chromia node running to work with the database. While experimenting, start the node with the --wipe flag, which deletes all records currently stored.

shell
chr node start --wipe

A one-to-one relationship requires records in two tables, library and address. The create_library operation defined earlier creates both.

The commands below call create_library twice, passing the required arguments each time, to create two pairs of related records:

shell
chr tx --await create_library "Biblioteca Apostolica Vaticana" "Città del Vaticano" "Vatican" '"001201"'
shell
chr tx --await create_library "New York Public Library" "476 5th Ave" "New York" '"10018"'

Each successful call should return:

shell
transaction with rid 0CAADC0937030F67C78003E2F9C1CA15E8C5D0C280ACA295CB8CD747AA09FBD9 was posted CONFIRMED

Querying new records

To fetch the new records, run get_all_library from the Chromia CLI:

shell
chr query get_all_library

The query returns:

json
[  [    "city": "Vatican",    "id": 2,    "library_name": "Biblioteca Apostolica Vaticana",    "postal_code": "001201",    "street": "Citta del Vaticano"  ],  [    "city": "New York",    "id": 4,    "library_name": "New York Public Library",    "postal_code": "10018",    "street": "476 5th Ave"  ]]
Written for this site from Chromia’s public course. Code samples are the course’s own.