Skip to content

Saving a resource

E. Lynette Rayle edited this page Oct 22, 2020 · 13 revisions

back to Introduction

Goals

  • Understand persistence.
  • Create and save a Book resource.
  • Inspect the database after persisting a resource.
  • Create and save a Page as a member of a Book.
  • Create and save CoverArt associated with a Book and observe its persistence before and after saving the Book.

Persistence

Introduction

One of the biggest conceptual differences between the Active Record and Data Mapper patterns is that while an Active Record object knows how to save itself, a Data Mapper object does not. Instead, a Data Mapper application can implement multiple persisters. Each persister accepts an object as an argument and saves it to persistent storage. The storage systems that are easily configured for Valkyrie so far include Postgres and Fedora.

One of the advantages of this pattern is that it provides an abstraction layer between an object in memory and the way it is persisted, whether that is to a database, a triple store, or some other system.

Reference: Valkyrie Persistence Documentation | postgres persister | shared specs/persister

Terminology

Save a book

Run the following in bundle exec rails console to save a new book resource...

> book = Book.new(alternate_ids: 'b1', title: 'Free Fall', author: 'Robert Crais', series: 'Elvis Cole/Joe Pike')
> saved_book = ValkyriePgDemo.pg_persister.save(resource: book)

Note that the book object has not changed. In particular, it does not have values assigned for id, created_at, or persisted?.

> book.alternate_ids
 => [#<Valkyrie::ID:... @id="b1"]
> book.id
 => nil
> book.created_at
 => nil
> book.persisted?
 => false

Note that the saved_book object now contains all of the attributes of the book object, plus the changes made to an object when it is persisted.

> saved_book.alternate_ids
 => [#<Valkyrie::ID:... @id="b1">]
> saved_book.id
 => #<Valkyrie::ID:... @id="_AN_ID_ASSIGNED_BY_POSTGRES_">
> saved_book.created_at
 => Mon, 09 Dec 2019 19:01:04 UTC +00:00
> saved_book.persisted?
 => true
> saved_book.title
 => ["Free Fall"]

Record the value of saved_book.id.to_s. You will use this in [Retrieving resources].

What happens in the database?

Valkyrie only adds one table to the postgres database: orm_resources. In the terminal, open the postgres command line with psql postgres. NOTE: To quit the postgres command line, type \q.

postgres=# \c valkyrie_pg_demo_development
postgres=# \dt
                   List of relations
 Schema |         Name         | Type  |     Owner     
--------+----------------------+-------+---------------
 public | ar_internal_metadata | table | valkyrie_user
 public | orm_resources        | table | valkyrie_user
 public | schema_migrations    | table | valkyrie_user
(3 rows)

Saving book creates a single row in table orm_resources in the postgres database. The metadata column holds the resource's data as json and the internal_resource column holds the model (e.g. 'Book').

postgres=# select * from orm_resources;

                  id                  |                                                   metadata                                                                                                     |         created_at         |         updated_at         | internal_resource | lock_version
--------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------+----------------------------+-------------------+--------------
c1a6dacf-a41f-4a04-ab71-af2ebf090e23 | {"title": ["Free Fall"], "author": ["Robert Crais"], "series": ["Elvis Cole/Joe Pike"], "cover_art": [], "new_record": [true], "alternate_ids": [{"id": "b1"}]} | 2019-12-18 17:06:30.192841 | 2019-12-18 17:06:30.192841 | Book              |

NOTES:

  • In general, attributes without values are not saved in the metadata json. In this case, member_ids was not included. Arrays and Sets are an empty array if they do not have values.
  • IDs are examples only. Record the actual id after saving using saved_book.id.to_s for use in [Retrieving resources].

FAILURE - Setting id before saving

Recall from [Understanding resources], it was stated that best practice is to not assign an id prior to saving and to let the datastore create the id during the persistence process. Try assigning one here to see what happens in postgres if you try to assign an id.

> book2 = Book.new(id: 'book_2', alternate_ids: 'b2', title: 'Lullaby Town', author: 'Robert Crais', series: 'Elvis Cole/Joe Pike')
> ValkyriePgDemo.pg_persister.save(resource: book2)
 => Valkyrie::Persistence::UnsupportedDatatype

Postgres' primary key column cannot save with the given ID book_2. To avoid this error, set the ID to be nil via resource.id = nil before you save it.

Save a Page and add it as a member of a Book

Run the following in bundle exec rails console to save a new page resource and update the book resource. Note that saved_book was created in a previous example:

> book = saved_book      
> page = Page.new(page_num: '1', structure: 'title page')
> page = ValkyriePgDemo.pg_persister.save(resource: page)  
# saves the new page resource
> book.member_ids = page.id
> book = ValkyriePgDemo.pg_persister.save(resource: book)  
# updates the existing book resource

Saving page creates a second row in the table orm_resources with the page data in the metadata column and internal_resource=Page. Saving the book resource updates its metadata to add the member_ids.

In the postgres command line:

postgres=# \c valkyrie_pg_demo_development
postgres=# select * from orm_resources;

                  id                  |                                                   metadata                                                                                                                                                                       |         created_at         |         updated_at         | internal_resource | lock_version
--------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------+----------------------------+-------------------+--------------
 844c5e0e-8a27-48c6-b69b-750e623f5290 | {"page_num": ["1"], "structure": ["title page"], "new_record": [true], "alternate_ids": []}                                                                                                                                      | 2019-12-18 17:07:31.577452 | 2019-12-18 17:07:31.577452 | Page              |             
 c1a6dacf-a41f-4a04-ab71-af2ebf090e23 | {"title": ["Free Fall"], "author": ["Robert Crais"], "series": ["Elvis Cole/Joe Pike"], "cover_art": [], "member_ids": [{"id": "844c5e0e-8a27-48c6-b69b-750e623f5290"}], "new_record": [false], "alternate_ids": [{"id": "b1"}]} | 2019-12-18 17:06:30.192841 | 2019-12-18 17:08:04.411385 | Book              |

Add CoverArt to the Book and save the Book

Run the following in bundle exec rails console to create a CoverArt resource in the Book resource and save the updated book:

> book.cover_art = CoverArt.new(artists: 'Mary GrandPré')
> book = ValkyriePgDemo.pg_persister.save(resource: book)
# updates the existing book resource

This does not add a new row in the table orm_resources, because cover_art has not been persisted in its own right; however, the metadata pertaining to cover_art has been added to the book resource. Saving the book resource updates its metadata to add the cover_art with the full cover_art resource data.

In postgres command line:

postgres=# \c valkyrie_pg_demo_development
postgres=# select * from orm_resources;

                  id                  |                                                   metadata                                                                                                                                                                                                                                                                                                                                                     |         created_at         |         updated_at         | internal_resource | lock_version
--------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------+----------------------------+-------------------+--------------
 844c5e0e-8a27-48c6-b69b-750e623f5290 | {"page_num": ["1"], "structure": ["title page"], "new_record": [true], "alternate_ids": []}                                                                                                                                                                                                                                                                                                                    | 2019-12-18 17:07:31.577452 | 2019-12-18 17:07:31.577452 | Page              |             
 c1a6dacf-a41f-4a04-ab71-af2ebf090e23 | {"title": ["Free Fall"], "author": ["Robert Crais"], "series": ["Elvis Cole/Joe Pike"], "cover_art": [{"id": null, "artists": ["Mary GrandPré"], "image_id": null, "created_at": null, "new_record": true, "updated_at": null, "alternate_ids": [], "internal_resource": "CoverArt"}], "member_ids": [{"id": "844c5e0e-8a27-48c6-b69b-750e623f5290"}], "new_record": [false], "alternate_ids": [{"id": "b1"}]} | 2019-12-18 17:06:30.192841 | 2019-12-18 17:11:20.672987 | Book              |

Previous | Next