Skip to content

Queries

David Cliff edited this page Nov 2, 2021 · 10 revisions

Queries in Valkyrie are being added as they're needed for the logic of a digital repository, to better enable a wide array of inter-operable backend solutions. Currently, there are only seven that are supported by all adapters (shared spec):

Find All

Use Case: Re-indexing.

metadata_adapter.query_service.find_all # => [#<Book:0x007fe383479988>]

Find all of model

Use Case: Populate a collection drop-down with all available collections.

collection = metadata_adapter.persister.save(resource: Collection.new)
metadata_adapter.query_service.find_all_of_model(model: Collection) # => [collection]

Find by ID

Use Case: Finding an object for an item record page.

metadata_adapter.query_service.find_by(id: Valkyrie::ID.new("692ccfd5-414a-42f9-a8d5-c1605e7baef9"))
  # => #<Book:0x007fe383479988
         @id=#<Valkyrie::ID:0x007fe38353ac78 @id="692ccfd5-414a-42f9-a8d5-c1605e7baef9">,
         @title=[],
         @member_ids=[]>

Find many by IDs

Use Case: Retrieve a set of records for batch updating

[1] pry(main)> id_list = ["d0e66e56-83f0-48f4-9b44-6c921b65215a", "af32397f-33cc-4bb1-ac9b-7d1419e50234"]
[4] pry(main)> metadata_adapter.query_service.find_many_by_ids(ids: id_list)
  #=> [
       #<ScannedResource
         id=#<Valkyrie::ID:0x00007fc559785890 @id="d0e66e56-83f0-48f4-9b44-6c921b65215a">
         created_at="2019-10-22T21:31:17.194Z"
         updated_at="2019-10-22T21:31:17.194Z"
         title=["Curious Resource"]>,
       #<ScannedResource id=#<Valkyrie::ID:0x00007fc55976f040
         @id="af32397f-33cc-4bb1-ac9b-7d1419e50234">
         internal_resource="ScannedResource"
         created_at="2019-10-22T21:31:16.679Z" 
         title=["Culturally Significant Resource"]>
       ]

Find by Alternate ID

Use Case: Finding an object for an item record page, based on a ARK, DOI, or other kind of ID that isn't the Valkyrie ID.

metadata_adapter.query_service.find_by_alternate_identifier(alternate_identifier: "abc123"))
  # => #<Book:0x007fe383479988
         @id=#<Valkyrie::ID:0x007fe38353ac78 @id="692ccfd5-414a-42f9-a8d5-c1605e7baef9">,
         @alternate_identifier=["abc123"],
         @title=[],
         @member_ids=[]>

Find all Members

Use Case: List all members on a record page.

child_book = metadata_adapter.persister.save(resource: Book.new)
parent_book = metadata_adapter.persister.save(resource: Book.new(member_ids: child_book.id))
metadata_adapter.query_service.find_members(resource: parent_book) # => [child_book]

Find all Parents

Use Case: Clean-up parent associations when deleting a child.

child_book = metadata_adapter.persister.save(resource: Book.new)
parent_book = metadata_adapter.persister.save(resource: Book.new(member_ids: child_book.id))
metadata_adapter.query_service.find_parents(resource: child_book) # => [parent_book]

Find References By

Use Case: Find associated objects by a property, but not in order.

referenced_book = metadata_adapter.persister.save(resource: Book.new)
book = metadata_adapter.persister.save(resource: Book.new(referenced_book_id:
referenced_book.id))
metadata_adapter.query_service.find_references_by(resource: book, property: :referenced_book_id) # => [referenced_book]

Find Inverse References By

Use Case: Find everything that references me, so I can clean them up when being deleted.

referenced_book = metadata_adapter.persister.save(resource: Book.new)
book = metadata_adapter.persister.save(resource: Book.new(referenced_book_id:
referenced_book.id))
metadata_adapter.query_service.find_inverse_references_by(resource: referenced_book, property: :referenced_book_id) # => [book]

Custom queries

In order to add custom queries to Valkyrie, the queries themselves must be Classes which follow a number of conventions. Typically, a query is added into app/queries for an Application, such as app/queries/find_by_my_query.rb:

class FindByMyQuery
  def self.queries
    [:find_by_my_query]
  end

  attr_reader :query_service
  delegate :resource_factory, to: :query_service
  delegate :orm_class, to: :resource_factory
  def initialize(query_service:)
    @query_service = query_service
  end

  def find_by_my_query(my_value)
    internal_array = "[\"#{my_value}\"]"
    run_query(query, internal_array)
  end

  def query
    <<-SQL
      select * FROM orm_resources WHERE
      metadata->'my_property' @> ?
    SQL
  end

  def run_query(query, *args)
    orm_class.find_by_sql(([query] + args)).lazy.map do |object|
      resource_factory.to_resource(object: object)
    end
  end
end

In this example, FindByMyQuery permits a query to be structured for PostgreSQL which queries for some arbitrary metadata property (my_property). The value of this property is passed in the method identical to the Class name in the snake case find_by_my_query, and the SQL query itself merely searches for an equal value in the resource metadata. Also note that this method name symbolized and exposed within an array returned from the Class method self.queries. At this time, this structure is currently not enforced or validated.

With regards to how this is loaded into the query_service, one must add or modify the following within config/initializers/valkyrie.rb:

[FindByMyQuery].each do |query_handler|
    Valkyrie.config.metadata_adapter.query_service.custom_queries.register_query_handler(query_handler)
  end

Now, one can issue the query using the following invocation:

query_service.custom_queries.find_by_my_query("foo")

As with the standard queries outlined above, resource_factory will ensure that elements in the result set are cast into the appropriate Valkyrie Resource Class.

Clone this wiki locally