Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reverse Collection membership #901

Merged
merged 3 commits into from
Dec 9, 2016
Merged

Reverse Collection membership #901

merged 3 commits into from
Dec 9, 2016

Conversation

escowles
Copy link
Contributor

@escowles escowles commented Aug 16, 2016

Improve scalability of Collections by linking from Objects to Collections (instead of linking from Collections to their member Objects).

This PR removes the existing functionality to link from Collections to Objects. Objects are typically members of one or a small number of Collections, but some Collections can have 10,000 or more member Objects. While there is ongoing work to address the issue, Fedora performance for Collections that link to that many Objects with pcdm:hasMember is poor, so this PR dramatically improves performance by avoiding that bottleneck.

@projecthydra/sufia-code-reviewers

@@ -115,6 +115,7 @@ def form_class

def build_form
@form = form_class.new(curation_concern, current_ability)
@collections = Collection.all
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return value of this method has changed, which may or may not be an issue. I'm also concerned about adding another instance variable for the view to use.

Suggestion:

  1. Add form_class#collections_for_select method that can be referenced in the view

@escowles
Copy link
Contributor Author

Screenshots demonstrating new functionality:

Selecting one or more collections on the Work create or edit form:
object-new-edit

Listing collections a Work is a member of on the Work show page:
object-show

Collection membership is a facet in search results:
search-facet

@escowles
Copy link
Contributor Author

@jeremyf thanks for your comments — I've pushed another commit to address them.

@escowles escowles force-pushed the member_of_replace branch 3 times, most recently from 27ee033 to e18cb74 Compare August 23, 2016 11:15
@pgwillia
Copy link

This is still preliminary for us but in order to get this to work for our sufia 7.1.0 based app, in our project's Gemfile we had to include

gem 'hydra-pcdm', github:'projecthydra/hydra-pcdm', branch: :master
gem 'hydra-works', github:'projecthydra/hydra-works', branch: :master

and make one more modification to curation_concerns

diff --git a/app/models/concerns/curation_concerns/collection.rb b/app/models/concerns/curation_concerns/
index 2c948c9..5b4ab12 100644
--- a/app/models/concerns/curation_concerns/collection.rb
+++ b/app/models/concerns/curation_concerns/collection.rb
@@ -9,6 +9,11 @@ module CurationConcerns
     def add_members(new_member_ids)
       return if new_member_ids.nil? || new_member_ids.empty?
       members << ActiveFedora::Base.find(new_member_ids)
+      members.each do |m|
+        m.member_of_collections << self 
+        m.member_of_collection_ids << id
+        m.save!
+      end
     end
   end
 end

@escowles escowles changed the title [WIP] Reverse Collection membership Reverse Collection membership Aug 29, 2016
@escowles
Copy link
Contributor Author

@pgwillia Thanks for bringing that up. I think it's better to leave the members/add_members alone (so Collections can still have child Collections if anyone wants to do that), but I've added some helper methods like what you outlined to add a batch of objects to a collection. See b6fa608

@tpendragon
Copy link
Contributor

We need an automated data migration method to do this.

@pgwillia
Copy link

pgwillia commented Aug 31, 2016

@escowles The problem that I was trying to solve is that Sufia doesn't use an Actor like your Plum does, so needed a place to populate member_of_collections. Where the collection added members seemed logical to me. Shouldn't the inverted collection membership apply to all members regardless of class?

Sufia also needs member_of_collection_ids set. Would you be agreeable to adding this to the add_member_objects helper method?

@pgwillia
Copy link

pgwillia commented Aug 31, 2016

Sufia calls

# app/controllers/concerns/curation_concerns/collections_controller_behavior.rb
80     def create
81       @collection.apply_depositor_metadata(current_user.user_key)
82       add_members_to_collection unless batch.empty?
...
216       def add_members_to_collection(collection = nil)
217         collection ||= @collection
218         collection.add_members batch
219       end

So I think that member_of_collections should be set somewhere in curation_concerns.

@pgwillia
Copy link

pgwillia commented Sep 1, 2016

Did @tpendragon last commit to this branch step on some of @escowles work? b6fa608 doesn't seem to exist.

@tpendragon
Copy link
Contributor

Er, maybe? @escowles can you check? I thought I just got rid of the AF lock.

pgwillia added a commit to ualbertalib/Hydranorth2 that referenced this pull request Sep 1, 2016
…ixes ualbertalib/HydraNorth#1126

 * use latest fcrepo4 (4.6.0)
 * add debugging tools to Gemfile
 * still need to override curation_concerns for some missing behaviour
 * configure Collection faceting
@tpendragon
Copy link
Contributor

@pgwillia Looks like @escowles pushed up the missing code.

@escowles
Copy link
Contributor Author

Are there any outstanding issues with this PR? @pgwillia @jeremyf @projecthydra/sufia-code-reviewers ? If not, I can rebase it to address the merge conflicts.

@mjgiarlo
Copy link
Member

@escowles that sounds like a no to me. Rebase away!

@jeremyf
Copy link
Contributor

jeremyf commented Oct 5, 2016

@escowles We have a broken build due to dependencies on RDF.

@jcoyne
Copy link
Contributor

jcoyne commented Oct 24, 2016

@escowles this last change (requiring RDF 2.1) is going to be a breaking change for folks who want the ordering behavior present in RDF 1.

@escowles
Copy link
Contributor Author

@jcoyne I know — but I thought it was unavoidable because we're requiring a version of AF that requires RDF 2.x.

But I just checked and noticed that curation_concerns.gemspec is only requiring AF >= 10.3.0.rc1, whereas the test app Gemfile.extra is requiring 11.0.0.rc7. I'll see if I can relax the Gemfile.extra to match the gemspec and get it working with RDF 1.x.

@tpendragon
Copy link
Contributor

tpendragon commented Nov 2, 2016

@escowles Were those two tests really unused? They seem to be around the collection show page "remove member" functionality.

@escowles
Copy link
Contributor Author

escowles commented Nov 2, 2016

@tpendragon I removed the tests because this PR removes the underlying functionality, since collection membership is now managed through the work edit form instead of through the collection edit form.

I'm not opposed to reimplementing that functionality if people think it's useful. But this PR is already big enough I thought it was better to not add new stuff to it.

@tpendragon
Copy link
Contributor

@escowles I think we should maintain the current UI's functionality - it feels unexpected that it would change.

@@ -10,6 +10,11 @@ def generate_solr_document
# Index the size of the collection in bytes
solr_doc[Solrizer.solr_name(:bytes, STORED_LONG)] = object.bytes
solr_doc['thumbnail_path_ss'] = thumbnail_path

object.in_collections.each do |col|
solr_doc['member_of_collection_ids_ssim'] = col.id
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can collections only belong to one other collection? Is that why you are using = here instead of <<?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this should be using << instead of = to allow the object to be in multiple collections — I'll update that.

Copy link
Member

@mjgiarlo mjgiarlo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few last changes to suggest. Thanks for working on this!

@@ -0,0 +1,6 @@
<% if @form.respond_to? :member_of_collection_ids %>
<div class="row" with-headroom">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@escowles You've either got an extraneous or a missing " here.

@@ -45,6 +45,7 @@ Gem::Specification.new do |spec|
spec.add_dependency 'dry-struct', '~> 0.1'
spec.add_dependency 'flot-rails', '~> 0.0.7'
spec.add_dependency 'highcharts-rails'
spec.add_dependency 'hydra-pcdm', '~> 0.9.0'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of pinning hydra-pcdm in CC, should we update the hydra-works gemspec and let CC's dependence on HW pick up the right version of HP?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've uddated samvera/hydra-works#303 to make HW require hydra-pcdm >= 0.9.0. I can update this PR to use that once it's released.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merged, @escowles. Thanks! HW now needs a release and this PR can then point at the new release.

@@ -1,4 +1,6 @@
# placeholder to use for pinning against specific gem commit references
gem 'hydra-pcdm', github:'projecthydra/hydra-pcdm', branch: 'master'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these still needed? I'd like to see these taken out before we merge this PR -- and then we can cut the 2.0 release soon thereafter.

@mjgiarlo
Copy link
Member

@escowles Would you like to squash the commits, or do you think they're good logical chunks that should remain separate? I could go either way.

@@ -9,6 +9,7 @@ def self.build(curation_concern, current_user)

def self.stack_actors(curation_concern)
[AddToCollectionActor,
AddAsMemberOfCollectionsActor,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we making this a new actor and not replacing AddToCollectionActor?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intention was to leave the existing membership functionality for Collections to continue to have member Collections. But I could just replace AddToCollectionActor if that makes more sense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think Collections use the ActorStack, so I think we can get rid of it, right?

@escowles
Copy link
Contributor Author

escowles commented Dec 1, 2016

@mjgiarlo I've rebased and squashed this PR, now depending on hydra-works 0.15.
@jcoyne Does my answer above address your concern, or would you like me to replace AddToCollectionActor?

@@ -9,6 +9,7 @@ def self.build(curation_concern, current_user)

def self.stack_actors(curation_concern)
[AddToCollectionActor,
AddAsMemberOfCollectionsActor,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think Collections use the ActorStack, so I think we can get rid of it, right?

@escowles
Copy link
Contributor Author

escowles commented Dec 7, 2016

@jcoyne I've removed the AddToCollectionActor to avoid that duplication.


def add_member_objects(new_member_ids)
Array(new_member_ids).each do |member_id|
member = ActiveFedora::Base.find(member_id, cast: true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cast: true doesn't do anything (anymore). please remove that.

@@ -10,5 +10,17 @@ def add_members(new_member_ids)
return if new_member_ids.nil? || new_member_ids.empty?
members << ActiveFedora::Base.find(new_member_ids)
end

def add_member_objects(new_member_ids)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add some documentation here about how this adds the association on the member side. If you wouldn't mind, please also add some documentation to add_members too.

*presenter_factory_arguments)
end

def member_of_collection_presenters
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add method documentation here.

@@ -84,8 +82,12 @@ def composite_presenter_class

# @return [Array<CollectionPresenter>] presenters for the collections that this work is a member of
def collection_presenters
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this method still needed?

<% end %>
</li>
<% end %>
<% if can? :collect, document %>
<% if can?(:collect, document) && document.collection? %>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this check required. Isn't this used on a show page for a collection (which ensures that document is a Collection)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this template used at all?

@@ -1,7 +1,6 @@
<% provide :page_title, curation_concern_page_title(@collection) %>

<h1>Edit <%= @collection %> <span class="human_readable_type">(<%= @collection.human_readable_type %>)</span></h1>

<h1>Edit <%= @collection.first_title %> <span class="human_readable_type">(<%= @collection.human_readable_type %>)</span></h1>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use @collection.to_s for the title rather than first_title.

@@ -0,0 +1,6 @@
<% if @form.respond_to? :member_of_collection_ids %>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to check this? Shouldn't it always be present?

@@ -0,0 +1,12 @@
<% if defined?(presenter.member_of_collection_presenters) && presenter.member_of_collection_presenters.size > 0 %>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to check for defined?(presenter.member_of_collection_presenters)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is called from _attributes.html.erb for everything (including FileSets that don't have a member_of_collection_presenters method). We can move this logic into _attribute.html.erb if that seems better.

<td>
<ul class="member_of_collections">
<% presenter.member_of_collection_presenters.each do |p| %>
<li><%= link_to p.title.first, main_app.collection_path(p.id), class: 'collection-link' %></li>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we encapsulate p.title.first in a method (#to_s perhaps)?

@escowles
Copy link
Contributor Author

escowles commented Dec 8, 2016

@jcoyne I've added some docs and removed some unnecessary methods and checks leftover from when I was trying to support both the member and member_of membership modes.

Copy link
Contributor

@jcoyne jcoyne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one more thing. Then we can merge it.

@@ -0,0 +1,12 @@
<% if defined?(presenter.member_of_collection_presenters) && presenter.member_of_collection_presenters.size > 0 %>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Let's get rid of this check here and put it in the _attributes partial.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I've moved the check into the _attributes partial.

@jcoyne
Copy link
Contributor

jcoyne commented Dec 9, 2016

I'm dismissing @mjgiarlo's review because all those changes have been made.

@jcoyne jcoyne dismissed mjgiarlo’s stale review December 9, 2016 14:16

All these changes have been made.

@jcoyne jcoyne merged commit 613651e into master Dec 9, 2016
@jcoyne jcoyne removed the in progress label Dec 9, 2016
@jcoyne jcoyne deleted the member_of_replace branch December 9, 2016 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants