Aggregations API Documentation - samvera/hydra-works GitHub Wiki
Warning: The API is under active development. It may have changed since the last update to the document.
Last Update: 2015-12-18
Table of Contents
- Introduction
- Defining an aggregation
- Methods for members
- Methods for ordered_members
- Enforcing the Hydra-Works models
- Traversing the model
Introduction
Terminology
- members - A set with each member in the set existing only once. Order is unimportant and not guaranteed to be the same for each retrieval.
- ordered_members - An ordered list where the same member may exist in the list multiple times. The order is precise and will be returned in the same order for every retrieval.
NOTE: There could be other terms for the roles that members and ordered_members plays depending on the name given the aggregation when it is defined. Since PCDM and Works defines aggregations with name :members, the examples in this document use the accessor methods associated with that name. See Defining an aggregation for more information.
General expectations
- members should always be the set view of ordered_members and may have additional members not in ordered_members
- the set of unique ordered_members will be a subset of members
Example:
- ordered_members => A, C, D, C, D --> unique set = A, C, D
- members => A, B, C, D --> has unique set from order_members + B which is only in members
NOTE: You will not see a member in ordered_members that is not in members.
Interaction behaviors for members and ordered members when using add and delete methods
Add to members:
- add to members one object with << will NOT append the object to ordered_members
- add to members array of objects with += will NOT append any objects of the array to ordered_members
- set members with = will remove from ordered_members any object that is no longer in members
Add to ordered_members:
- append one object to ordered_members with << will add it to members set iff the object is not already a part of the members set
- append array of objects in order to ordered_members with += will add each object to members set iff the object is not already a part of the members set
- set array of objects in order to ordered_members with = will add each object to members set iff the object was not already added to the members set
Delete from members:
- delete one object from members with delete(obj) also deletes all instances of object from the ordered_members list
Delete from ordered_members:
- delete one object from ordered_members with delete(obj) will NOT delete anything from members
- delete one object from ordered_members with delete_at(obj,idx) will NOT delete anything from members
Defining an aggregation
ordered_aggregation :members,
has_member_relation: Vocab::PCDMTerms.hasMember,
class_name: 'ActiveFedora::Base',
type_validator: type_validator,
through: :list_source
where:
- name - (e.g. :members) - name of the list and determines the names of the accessor methods for the list. All the examples in this doc assume the aggregation was defined with
:members
which generates accessor methodsmembers
andordered_members
- has_member_relation - predicate to use in the triple for
<aggregation_uri> __has_member_relation__ <resource_uri>
, (e.g. Vocab::PCDMTerms.hasMember, RDF::Vocab::ORE.aggregates) - class_name - instances of this class can be added as a member (using 'ActiveFedora::Base' covers all PCDM and Hydra:Works classes)
- type_validator - (optional) name of method that validates members before adding
- through - contained RDF source in which proxies are stored in Fedora
Methods for members
NOTE: Examples assume that there is an instance of Hydra::Works::Collection col1 and instances of Hydra::Works::Work work1 - work5.
Add methods
col1.members = [] # => []
col1.members = [work1,work2] # => [work1,work2]
col1.members << work3 # => [work1,work3,work2]
col1.members += [work4,work5] # => [work1,work4,work5,work2,work3]
*NOTE: members is a set. Adding a member that is already in the set leaves members unchanged.
col1.members = [work1,work2] # => [work1,work2]
col1.members << work1 # => [work1,work2]
Delete methods
NOTE: Because deleting from members can also impact ordered_members, examples show effects on both.
# setup
col1.members = [] # members => [] ordered_members => []
col1.ordered_members = [work1,work2,work3] # members => [work1,work2,work3] ordered_members => [work1,work2,work3]
# delete
col1.members.delete(work2) # members => [work1,work3] ordered_members = [work1,work3]
Get methods
NOTE: members is an unordered set. Retrieving members multiple times could result in a different order.
col1.members = [work1,work2,work3,work4] # => [work1,work2,work3,work4]
col1.members # => [work1,work3,work4,work5]
col1.members # => [work3,work4,work1,work5]
col1.members # => [work4,work1,work3,work5]
NOTE: Although members is not an Array, it supports many of the same methods as Array. The following methods will retrieve individual items from members. Since members may not return the same ordering with each call. If you are going to access individual members in this way, it is recommended that you store the results of col1.members in a variable first.
members = col1.members # -> [work4,work1,work3,work5]
members.first # => work4
members.last # => work5
members[2] # => work3
You can try other Array methods too. For example, col1.members.each
is valid.
Methods for ordered_members
Add methods
NOTE: Results show changes in values for both members and ordered_members.
col1.members = [] # ordered_members => [] members => []
col1.ordered_members = [work1,work2] # ordered_members => [work1,work2] members => [work1,work2]
col1.ordered_members << work3 # ordered_members => [work1,work2,work3] members => [work1,work3,work2]
col1.ordered_members += [work4,work5] # ordered_members => [work1,work2,work3,work4,work5] members => [work1,work4,work5,work2,work3]
col1.ordered_members.insert_at(0,work2) # ordered_members => [work2,work1,work2,work3,work4,work5] members => [work1,work4,work5,work2,work3]
NOTE: ordered_members is an ordered list of some or all members in the members set. Adding a member that is already in the ordered list leaves members unchanged, but adds the member to ordered_members.
col1.members = [] # ordered_members => [] members => []
col1.ordered_members = [work1,work2] # ordered_members => [work1,work2] members => [work1,work2]
col1.ordered_members << work1 # ordered_members => [work1,work2,work1] members => [work1,work2]
Delete methods
NOTE: Deleting from an ordered list removes the member from the ordered list effectively deleting the ordering, but leaves it as a member in the members set.
# setup
col1.members = [] # ordered_members => [] members => []
col1.ordered_members = [work1,work2,work3,work2] # ordered_members => [work1,work2,work3,work2] members => [work1,work2,work3]
# delete
col1.ordered_members.delete(work2) # ordered_members => [work1,work3] members = [work1,work2,work3]
col1.ordered_members.delete_at(1) # ordered_members => [work1] members = [work1,work2,work3]
Get methods
NOTE: ordered_members is an ordered list. Retrieving ordered_members multiple times will always result in the same order.
col1.ordered_members = [work1,work2,work3,work2] # => [work1,work2,work3,work2]
col1.ordered_members # => [work1,work2,work3,work2]
col1.ordered_members # => [work1,work2,work3,work2]
col1.ordered_members # => [work1,work2,work3,work2]
NOTE: Although ordered_members is not an Array, it supports many of the same methods as Array. The following methods will retrieve individual items from ordered_members.
col1.ordered_members.first # => work1
col1.ordered_members.last # => work2
col1.ordered_members[2] # => work3
You can try other Array methods too. For example, col1.ordered_members.each
is valid.
Move methods
NOTE: To move, use delete and insert methods.
col1.ordered_members = [work1,work2,work3,work2] # => [work1,work2,work3,work2]
obj = col1.delete_at[3] # => [work1,work2,work3]
col1.insert_at[obj,0] # => [work2,work1,work2,work3]
Enforcing the Hydra-Works models
Terminology
works-collection is a pcdm-collection that can have collections and/or works as members. It is used to organization.
work is a pcdm-object that can have works and/or filesets as members. A work does not have files representing the work. Each file of the work is added as a separate fileset.
fileset is a pcdm-object that can hold binary files. Each fileset has one content file that is the uploaded (or primary) document. Each fileset can also hold binary files representing alternate forms of the original content (i.e. derivatives).
Attempting to add members that are inconsistent with the model
# Try to add a file set to a collection
col1.members << fset1 # ### TODO This should produce an error message, but doesn't. See Note below.
NOTE: The above should produce an error message and NOT add the file set to the collection. See issue hw-257.
# Try to add a collection to itself
col1.members << col1 # ArgumentError: Collection with ID: col-1 failed to pass AncestorChecker validation
# Try to add a collection to its ancestor
col1.members << col2
col2.members << col3
col3.members << col1 # ArgumentError: Collection with ID: col-1 failed to pass AncestorChecker validation
# Try to add a collection to a work ### TODO This message isn't quite right. Not clear. See Note below.
work1.members << col1 # ActiveFedora::AssociationTypeMismatch: #<Collection:0x007fc234ef9950> is not a PCDM object.
NOTE: The above message would be more precise using 'is not a Hydra::Works work or file set'. See issue hw-259.
# Try to add a work to itself
work1.members << work1 # ArgumentError: BibliographicWork with ID: work-1 failed to pass AncestorChecker validation
# Try to add a work to its ancestor
work1.members << work2
work2.members << work3
work3.members << work1 # ArgumentError: BibliographicWork with ID: work-1 failed to pass AncestorChecker validation
Traversing the model
Get specific types of members
# setup
col1.members = [col2,col3,work1,work2]
work1.members = [work2,work3,fset1,fset2]
# get members by type
col1.members # => [work1,work2,col2,col3]
col1.collections # => [col2,col3]
col1.works # => [work1,work2]
work1.members # => [work2,work3,fset1,fset2]
work1.works # => [work2,work3]
work1.file_sets # => [fset1,fset2]
Get membership information
# setup
col1.members = [col5,work4]
col2.members = [col5]
col3.members = [col4,col5,work4]
work1.members = [work4,fset1]
work2.members = [work4,work3,work6,fset2,fset3]
# get membership
col4.in_collections # => [col3]
col5.in_collections # => [col1,col2,col3]
work4.in_collections # => [col1,col3]
work4.in_works # => [work1,work2]
# get the work a file_set is in
fset1.in_work # => work1 ### TODO This doesn't work. Neither does in_works which always returns []. See Note below.
NOTE: The above method or a similar method should return the work in which the file set is a member. See issue h2-258.