active-fedora 2.3.0 → 2.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CONSOLE_GETTING_STARTED.textile +92 -59
- data/Gemfile.lock +1 -2
- data/README.textile +2 -2
- data/config/fedora.yml +1 -1
- data/config/predicate_mappings.yml +17 -20
- data/lib/active_fedora.rb +2 -5
- data/lib/active_fedora/samples/hydra-mods_article_datastream.rb +3 -3
- data/lib/active_fedora/samples/sample_af_obj_relationship_query_param.rb +11 -0
- data/lib/active_fedora/samples/special_thing.rb +4 -2
- data/lib/active_fedora/semantic_node.rb +254 -15
- data/lib/active_fedora/version.rb +1 -1
- data/spec/fixtures/rails_root/config/fedora.yml +4 -4
- data/spec/integration/nokogiri_datastream_spec.rb +2 -1
- data/spec/integration/semantic_node_spec.rb +259 -2
- data/spec/unit/active_fedora_spec.rb +2 -2
- data/spec/unit/semantic_node_spec.rb +13 -6
- metadata +8 -6
@@ -0,0 +1,11 @@
|
|
1
|
+
class SampleAFObjRelationshipQueryParam < ActiveFedora::Base
|
2
|
+
#points to all parents linked via is_member_of
|
3
|
+
has_relationship "parents", :is_member_of
|
4
|
+
#returns only parents that have a level value set to "series"
|
5
|
+
has_relationship "series_parents", :is_member_of, :query_params=>{:q=>{"level_t"=>"series"}}
|
6
|
+
#returns all parts
|
7
|
+
has_relationship "parts", :is_part_of, :inbound=>true
|
8
|
+
#returns only parts that have level to "series"
|
9
|
+
has_relationship "series_parts", :is_part_of, :inbound=>true, :query_params=>{:q=>{"level_t"=>"series"}}
|
10
|
+
has_bidirectional_relationship "bi_series_parts", :has_part, :is_part_of, :query_params=>{:q=>{"level_t"=>"series"}}
|
11
|
+
end
|
@@ -5,10 +5,12 @@ require "active_fedora/samples/marpa-dc_datastream.rb"
|
|
5
5
|
|
6
6
|
# This is an example of an ActiveFedora Model
|
7
7
|
#
|
8
|
-
# Some of the datastream ids were chosen based on the Hydra modeling conventions. You don't have to follow them in your work.
|
8
|
+
# Some of the datastream ids were chosen based on the Hydra modeling conventions. You don't have to follow them in your work.
|
9
|
+
# ActiveFedora itself has no notion of those conventions, but we do encourage you to use them.
|
9
10
|
#
|
10
11
|
# The Hydra conventions encourage you to have a datastream with this dsid whose contents are descriptive metadata like MODS or Dublin Core. They especially encourage MODS.
|
11
|
-
# The
|
12
|
+
# The descMetadata dsid is a Hydra convention for a datastream with descriptive metadata contents, like MODS or Dublin Core. They especially encourage MODS.
|
13
|
+
# The rightsMetadata dsid is also a convention provided by the Hydra Common Metadata "content model"
|
12
14
|
#
|
13
15
|
# For more info on the Hydra conventions, see the documentation on "Common Metadata content model" in https://wiki.duraspace.org/display/hydra/Hydra+content+models+and+disseminators
|
14
16
|
# Note that on the wiki, "content model" is often used to refer to Fedora CModels and/or abstract/notional models. The Common Metadata content model is an example of this.
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ActiveFedora
|
2
2
|
module SemanticNode
|
3
3
|
include MediaShelfClassLevelInheritableAttributes
|
4
|
-
ms_inheritable_attributes :class_relationships, :internal_uri
|
4
|
+
ms_inheritable_attributes :class_relationships, :internal_uri
|
5
5
|
|
6
6
|
attr_accessor :internal_uri, :named_relationship_desc, :relationships_are_dirty, :load_from_solr
|
7
7
|
|
@@ -442,7 +442,54 @@ module ActiveFedora
|
|
442
442
|
end
|
443
443
|
xml.to_s
|
444
444
|
end
|
445
|
-
|
445
|
+
|
446
|
+
# Returns a solr query for retrieving objects specified in a relationship.
|
447
|
+
# It enables the use of query_params defined within a relationship to attach a query filter
|
448
|
+
# on top of just the predicate being used.
|
449
|
+
# Instead of this method you can also use the helper method
|
450
|
+
# [relationship_name]_query, i.e. method "parts_query" for relationship "parts".
|
451
|
+
# @param [String] The name of the relationship defined in the model
|
452
|
+
# @return [String]
|
453
|
+
# @example
|
454
|
+
# Class SampleAFObjRelationshipQueryParam < ActiveFedora::Base
|
455
|
+
# #points to all parents linked via is_member_of
|
456
|
+
# has_relationship "parents", :is_member_of
|
457
|
+
# #returns only parents that have a level value set to "series"
|
458
|
+
# has_relationship "series_parents", :is_member_of, :query_params=>{:q=>{"level_t"=>"series"}}
|
459
|
+
# end
|
460
|
+
# s = SampleAFObjRelationshipQueryParam.new
|
461
|
+
# obj = ActiveFedora::Base.new
|
462
|
+
# s.parents_append(obj)
|
463
|
+
# s.series_parents_query
|
464
|
+
# #=> "(id:changeme\\:13020 AND level_t:series)"
|
465
|
+
# SampleAFObjRelationshipQueryParam.named_relationship_query("series_parents")
|
466
|
+
# #=> "(id:changeme\\:13020 AND level_t:series)"
|
467
|
+
def named_relationship_query(relationship_name)
|
468
|
+
query = ""
|
469
|
+
if self.class.is_bidirectional_relationship?(relationship_name)
|
470
|
+
id_array = []
|
471
|
+
predicate = outbound_named_relationship_predicates["#{relationship_name}_outbound"]
|
472
|
+
if !outbound_relationships[predicate].nil?
|
473
|
+
outbound_relationships[predicate].each do |rel|
|
474
|
+
id_array << rel.gsub("info:fedora/", "")
|
475
|
+
end
|
476
|
+
end
|
477
|
+
query = self.class.bidirectional_named_relationship_query(pid,relationship_name,id_array)
|
478
|
+
elsif outbound_relationship_names.include?(relationship_name)
|
479
|
+
id_array = []
|
480
|
+
predicate = outbound_named_relationship_predicates[relationship_name]
|
481
|
+
if !outbound_relationships[predicate].nil?
|
482
|
+
outbound_relationships[predicate].each do |rel|
|
483
|
+
id_array << rel.gsub("info:fedora/", "")
|
484
|
+
end
|
485
|
+
end
|
486
|
+
query = self.class.outbound_named_relationship_query(relationship_name,id_array)
|
487
|
+
elsif inbound_relationship_names.include?(relationship_name)
|
488
|
+
query = self.class.inbound_named_relationship_query(pid,relationship_name)
|
489
|
+
end
|
490
|
+
query
|
491
|
+
end
|
492
|
+
|
446
493
|
module ClassMethods
|
447
494
|
|
448
495
|
# Allows for a relationship to be treated like any other attribute of a model class. You define
|
@@ -452,7 +499,10 @@ module ActiveFedora
|
|
452
499
|
# class AudioRecord < ActiveFedora::Base
|
453
500
|
#
|
454
501
|
# has_relationship "oral_history", :has_part, :inbound=>true, :type=>OralHistory
|
502
|
+
# # returns all similar audio
|
455
503
|
# has_relationship "similar_audio", :has_part, :type=>AudioRecord
|
504
|
+
# #returns only similar audio with format wav
|
505
|
+
# has_relationship "similar_audio_wav", :has_part, :query_params=>{:q=>"format_t"=>"wav"}
|
456
506
|
#
|
457
507
|
# The first two parameters are required:
|
458
508
|
# name: relationship name
|
@@ -461,6 +511,7 @@ module ActiveFedora
|
|
461
511
|
# possible parameters
|
462
512
|
# :inbound => if true loads an external relationship via Solr (defaults to false)
|
463
513
|
# :type => The type of model to use when instantiated an object from the pid in this relationship (defaults to ActiveFedora::Base)
|
514
|
+
# :query_params => Additional filters to be attached via a solr query (currently only :q implemented)
|
464
515
|
#
|
465
516
|
# If inbound is true it expects the relationship to be defined by another object's RELS-EXT
|
466
517
|
# and to load that relationship from Solr. Otherwise, if inbound is true the relationship is stored in
|
@@ -472,22 +523,24 @@ module ActiveFedora
|
|
472
523
|
# For the oral_history relationship in the example above the following helper methods are created:
|
473
524
|
# oral_history: returns array of OralHistory objects that have this AudioRecord with predicate :has_part
|
474
525
|
# oral_history_ids: Return array of pids for OralHistory objects that have this AudioRecord with predicate :has_part
|
526
|
+
# oral_history_query: Return solr query that can be used to retrieve related objects as solr documents
|
475
527
|
#
|
476
528
|
# For the outbound relationship "similar_audio" there are two additional methods to append and remove objects from that relationship
|
477
529
|
# since it is managed internally:
|
478
530
|
# similar_audio: Return array of AudioRecord objects that have been added to similar_audio relationship
|
479
531
|
# similar_audio_ids: Return array of AudioRecord object pids that have been added to similar_audio relationship
|
532
|
+
# similar_audio_query: Return solr query that can be used to retrieve related objects as solr documents
|
480
533
|
# similar_audio_append: Add an AudioRecord object to the similar_audio relationship
|
481
534
|
# similar_audio_remove: Remove an AudioRecord from the similar_audio relationship
|
482
535
|
def has_relationship(name, predicate, opts = {})
|
483
536
|
opts = {:singular => nil, :inbound => false}.merge(opts)
|
484
537
|
if opts[:inbound] == true
|
485
|
-
raise "Duplicate use of predicate for named inbound relationship not allowed" if named_predicate_exists_with_different_name?(:inbound,name,predicate)
|
538
|
+
#raise "Duplicate use of predicate for named inbound relationship not allowed" if named_predicate_exists_with_different_name?(:inbound,name,predicate)
|
486
539
|
register_named_relationship(:inbound, name, predicate, opts)
|
487
540
|
register_predicate(:inbound, predicate)
|
488
541
|
create_inbound_relationship_finders(name, predicate, opts)
|
489
542
|
else
|
490
|
-
raise "Duplicate use of predicate for named outbound relationship not allowed" if named_predicate_exists_with_different_name?(:self,name,predicate)
|
543
|
+
#raise "Duplicate use of predicate for named outbound relationship not allowed" if named_predicate_exists_with_different_name?(:self,name,predicate)
|
491
544
|
register_named_relationship(:self, name, predicate, opts)
|
492
545
|
register_predicate(:self, predicate)
|
493
546
|
create_named_relationship_methods(name)
|
@@ -562,6 +615,160 @@ module ActiveFedora
|
|
562
615
|
opts.merge!({:predicate=>predicate})
|
563
616
|
named_relationships_desc[subject][name] = opts
|
564
617
|
end
|
618
|
+
|
619
|
+
# Returns a solr query for retrieving objects specified in an outbound relationship.
|
620
|
+
# This method is mostly used by internal method calls.
|
621
|
+
# It enables the use of query_params defined within a relationship to attach a query filter
|
622
|
+
# on top of just the predicate being used. Because it is static it
|
623
|
+
# needs the pids defined within RELS-EXT for this relationship to be passed in.
|
624
|
+
# If you are calling this method directly to get the query you should use the
|
625
|
+
# ActiveFedora::SemanticNode.named_relationship_query instead or use the helper method
|
626
|
+
# [relationship_name]_query, i.e. method "parts_query" for relationship "parts". This
|
627
|
+
# method would only be called directly if you had something like an array of outbound pids
|
628
|
+
# already in something like a solr document for object that has these relationships.
|
629
|
+
# @param [String] The name of the relationship defined in the model
|
630
|
+
# @param [Array] An array of pids to include in the query
|
631
|
+
# @return [String]
|
632
|
+
# @example
|
633
|
+
# Class SampleAFObjRelationshipQueryParam < ActiveFedora::Base
|
634
|
+
# #points to all parents linked via is_member_of
|
635
|
+
# has_relationship "parents", :is_member_of
|
636
|
+
# #returns only parents that have a level value set to "series"
|
637
|
+
# has_relationship "series_parents", :is_member_of, :query_params=>{:q=>{"level_t"=>"series"}}
|
638
|
+
# end
|
639
|
+
# s = SampleAFObjRelationshipQueryParam.new
|
640
|
+
# obj = ActiveFedora::Base.new
|
641
|
+
# s.series_parents_append(obj)
|
642
|
+
# s.series_parents_query
|
643
|
+
# #=> "(id:changeme\\:13020 AND level_t:series)"
|
644
|
+
# SampleAFObjRelationshipQueryParam.outbound_named_relationship_query("series_parents",["id:changeme:13020"])
|
645
|
+
# #=> "(id:changeme\\:13020 AND level_t:series)"
|
646
|
+
def outbound_named_relationship_query(relationship_name,outbound_pids)
|
647
|
+
query = ActiveFedora::SolrService.construct_query_for_pids(outbound_pids)
|
648
|
+
subject = :self
|
649
|
+
if named_relationships_desc.has_key?(subject) && named_relationships_desc[subject].has_key?(relationship_name) && named_relationships_desc[subject][relationship_name].has_key?(:query_params)
|
650
|
+
query_params = format_query_params(named_relationships_desc[subject][relationship_name][:query_params])
|
651
|
+
if query_params[:q]
|
652
|
+
unless query.empty?
|
653
|
+
#substitute in the filter query for each pid so that it is applied to each in the query
|
654
|
+
query.sub!(/OR /,"AND #{query_params[:q]}) OR (")
|
655
|
+
#add opening parenthesis for first case
|
656
|
+
query = "(" + query
|
657
|
+
#add AND filter case for last element as well since no 'OR' following it
|
658
|
+
query << " AND #{query_params[:q]})"
|
659
|
+
else
|
660
|
+
query = query_params[:q]
|
661
|
+
end
|
662
|
+
end
|
663
|
+
end
|
664
|
+
query
|
665
|
+
end
|
666
|
+
|
667
|
+
# Returns a solr query for retrieving objects specified in an inbound relationship.
|
668
|
+
# This method is mostly used by internal method calls.
|
669
|
+
# It enables the use of query_params defined within a relationship to attach a query filter
|
670
|
+
# on top of just the predicate being used. Because it is static it
|
671
|
+
# needs the pid of the object that has the inbound relationships passed in.
|
672
|
+
# If you are calling this method directly to get the query you should use the
|
673
|
+
# ActiveFedora::SemanticNode.named_relationship_query instead or use the helper method
|
674
|
+
# [relationship_name]_query, i.e. method "parts_query" for relationship "parts". This
|
675
|
+
# method would only be called directly if you were working only with Solr and already
|
676
|
+
# had the pid for the object in something like a solr document.
|
677
|
+
# @param [String] The pid for the object that has these inbound relationships
|
678
|
+
# @param [String] The name of the relationship defined in the model
|
679
|
+
# @return [String]
|
680
|
+
# @example
|
681
|
+
# Class SampleAFObjRelationshipQueryParam < ActiveFedora::Base
|
682
|
+
# #returns all parts
|
683
|
+
# has_relationship "parts", :is_part_of, :inbound=>true
|
684
|
+
# #returns only parts that have level to "series"
|
685
|
+
# has_relationship "series_parts", :is_part_of, :inbound=>true, :query_params=>{:q=>{"level_t"=>"series"}}
|
686
|
+
# end
|
687
|
+
# s = SampleAFObjRelationshipQueryParam.new
|
688
|
+
# s.pid
|
689
|
+
# #=> id:changeme:13020
|
690
|
+
# s.series_parts_query
|
691
|
+
# #=> "is_part_of_s:info\\:fedora/changeme\\:13021 AND level_t:series"
|
692
|
+
# SampleAFObjRelationshipQueryParam.inbound_named_relationship_query(s.pid,"series_parts")
|
693
|
+
# #=> "is_part_of_s:info\\:fedora/changeme\\:13021 AND level_t:series"
|
694
|
+
def inbound_named_relationship_query(pid,relationship_name)
|
695
|
+
query = ""
|
696
|
+
subject = :inbound
|
697
|
+
if named_relationships_desc.has_key?(subject) && named_relationships_desc[subject].has_key?(relationship_name)
|
698
|
+
predicate = named_relationships_desc[subject][relationship_name][:predicate]
|
699
|
+
internal_uri = "info:fedora/#{pid}"
|
700
|
+
escaped_uri = internal_uri.gsub(/(:)/, '\\:')
|
701
|
+
query = "#{predicate}_s:#{escaped_uri}"
|
702
|
+
if named_relationships_desc.has_key?(subject) && named_relationships_desc[subject].has_key?(relationship_name) && named_relationships_desc[subject][relationship_name].has_key?(:query_params)
|
703
|
+
query_params = format_query_params(named_relationships_desc[subject][relationship_name][:query_params])
|
704
|
+
if query_params[:q]
|
705
|
+
query << " AND " unless query.empty?
|
706
|
+
query << query_params[:q]
|
707
|
+
end
|
708
|
+
end
|
709
|
+
end
|
710
|
+
query
|
711
|
+
end
|
712
|
+
|
713
|
+
# Returns a solr query for retrieving objects specified in a bidirectional relationship.
|
714
|
+
# This method is mostly used by internal method calls.
|
715
|
+
# It enables the use of query_params defined within a relationship to attach a query filter
|
716
|
+
# on top of just the predicate being used. Because it is static it
|
717
|
+
# needs the pids defined within RELS-EXT for the outbound relationship as well as the pid of the
|
718
|
+
# object for the inbound portion of the relationship.
|
719
|
+
# If you are calling this method directly to get the query you should use the
|
720
|
+
# ActiveFedora::SemanticNode.named_relationship_query instead or use the helper method
|
721
|
+
# [relationship_name]_query, i.e. method "bi_parts_query" for relationship "bi_parts". This
|
722
|
+
# method would only be called directly if you had something like an array of outbound pids
|
723
|
+
# already in something like a solr document for object that has these relationships.
|
724
|
+
# @param [String] The pid for the object that has these inbound relationships
|
725
|
+
# @param [String] The name of the relationship defined in the model
|
726
|
+
# @param [Array] An array of pids to include in the query
|
727
|
+
# @return [String]
|
728
|
+
# @example
|
729
|
+
# Class SampleAFObjRelationshipQueryParam < ActiveFedora::Base
|
730
|
+
# has_bidirectional_relationship "bi_series_parts", :has_part, :is_part_of, :query_params=>{:q=>{"level_t"=>"series"}}
|
731
|
+
# end
|
732
|
+
# s = SampleAFObjRelationshipQueryParam.new
|
733
|
+
# obj = ActiveFedora::Base.new
|
734
|
+
# s.bi_series_parts_append(obj)
|
735
|
+
# s.pid
|
736
|
+
# #=> "changeme:13025"
|
737
|
+
# obj.pid
|
738
|
+
# #=> id:changeme:13026
|
739
|
+
# s.bi_series_parts_query
|
740
|
+
# #=> "(id:changeme\\:13026 AND level_t:series) OR (is_part_of_s:info\\:fedora/changeme\\:13025 AND level_t:series)"
|
741
|
+
# SampleAFObjRelationshipQueryParam.bidirectional_named_relationship_query(s.pid,"series_parents",["id:changeme:13026"])
|
742
|
+
# #=> "(id:changeme\\:13026 AND level_t:series) OR (is_part_of_s:info\\:fedora/changeme\\:13025 AND level_t:series)"
|
743
|
+
def bidirectional_named_relationship_query(pid,relationship_name,outbound_pids)
|
744
|
+
outbound_named_relationship_query("#{relationship_name}_outbound",outbound_pids) + " OR (" + inbound_named_relationship_query(pid,"#{relationship_name}_inbound") + ")"
|
745
|
+
end
|
746
|
+
|
747
|
+
# This will transform and encode any query_params defined in a relationship method to properly escape special characters
|
748
|
+
# and format strings such as query string properly for a solr query
|
749
|
+
# @param [Hash] The has of expected query params (including at least :q)
|
750
|
+
# @return [String]
|
751
|
+
def format_query_params(query_params)
|
752
|
+
if query_params && query_params[:q]
|
753
|
+
add_query = ""
|
754
|
+
if query_params[:q].is_a? Hash
|
755
|
+
query_params[:q].keys.each_with_index do |key,index|
|
756
|
+
add_query << " AND " if index > 0
|
757
|
+
add_query << "#{key}:#{query_params[:q][key].gsub(/:/, '\\\\:')}"
|
758
|
+
end
|
759
|
+
elsif !query_params[:q].empty?
|
760
|
+
add_query = "#{query_params[:q]}"
|
761
|
+
end
|
762
|
+
query_params[:q] = add_query unless add_query.empty?
|
763
|
+
query_params
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
#Tests if the relationship name passed is in bidirectional
|
768
|
+
# @return [Boolean]
|
769
|
+
def is_bidirectional_relationship?(relationship_name)
|
770
|
+
named_relationships_desc[:self]["#{relationship_name}_outbound"] && named_relationships_desc[:inbound]["#{relationship_name}_inbound"]
|
771
|
+
end
|
565
772
|
|
566
773
|
# ** EXPERIMENTAL **
|
567
774
|
#
|
@@ -601,8 +808,8 @@ module ActiveFedora
|
|
601
808
|
class_eval <<-END
|
602
809
|
def #{name}(opts={})
|
603
810
|
opts = {:rows=>25}.merge(opts)
|
604
|
-
|
605
|
-
solr_result = SolrService.instance.conn.query(
|
811
|
+
query = self.class.inbound_named_relationship_query(self.pid,"#{name}")
|
812
|
+
solr_result = SolrService.instance.conn.query(query, :rows=>opts[:rows])
|
606
813
|
if opts[:response_format] == :solr
|
607
814
|
return solr_result
|
608
815
|
else
|
@@ -625,8 +832,18 @@ module ActiveFedora
|
|
625
832
|
def #{name}_from_solr
|
626
833
|
#{name}(:response_format => :load_from_solr)
|
627
834
|
end
|
835
|
+
def #{name}_solr_docs
|
836
|
+
#{name}(:response_format => :solr)
|
837
|
+
end
|
838
|
+
def #{name}_query
|
839
|
+
named_relationship_query("#{name}")
|
840
|
+
end
|
628
841
|
END
|
629
842
|
end
|
843
|
+
|
844
|
+
def relationship_has_query_params?(subject, relationship_name)
|
845
|
+
named_relationships_desc.has_key?(subject) && named_relationships_desc[subject].has_key?(relationship_name) && named_relationships_desc[subject][relationship_name].has_key?(:query_params)
|
846
|
+
end
|
630
847
|
|
631
848
|
def create_outbound_relationship_finders(name, predicate, opts = {})
|
632
849
|
class_eval <<-END
|
@@ -637,13 +854,20 @@ module ActiveFedora
|
|
637
854
|
id_array << rel.gsub("info:fedora/", "")
|
638
855
|
end
|
639
856
|
end
|
640
|
-
|
857
|
+
|
858
|
+
if opts[:response_format] == :id_array && !self.class.relationship_has_query_params?(:self,"#{name}")
|
641
859
|
return id_array
|
642
860
|
else
|
643
|
-
query =
|
861
|
+
query = self.class.outbound_named_relationship_query("#{name}",id_array)
|
644
862
|
solr_result = SolrService.instance.conn.query(query)
|
645
863
|
if opts[:response_format] == :solr
|
646
864
|
return solr_result
|
865
|
+
elsif opts[:response_format] == :id_array
|
866
|
+
id_array = []
|
867
|
+
solr_result.hits.each do |hit|
|
868
|
+
id_array << hit[SOLR_DOCUMENT_ID]
|
869
|
+
end
|
870
|
+
return id_array
|
647
871
|
elsif opts[:response_format] == :load_from_solr || self.load_from_solr
|
648
872
|
return ActiveFedora::SolrService.reify_solr_results(solr_result,{:load_from_solr=>true})
|
649
873
|
else
|
@@ -657,9 +881,15 @@ module ActiveFedora
|
|
657
881
|
def #{name}_from_solr
|
658
882
|
#{name}(:response_format => :load_from_solr)
|
659
883
|
end
|
884
|
+
def #{name}_solr_docs
|
885
|
+
#{name}(:response_format => :solr)
|
886
|
+
end
|
887
|
+
def #{name}_query
|
888
|
+
named_relationship_query("#{name}")
|
889
|
+
end
|
660
890
|
END
|
661
891
|
end
|
662
|
-
|
892
|
+
|
663
893
|
# Generates relationship finders for predicates that point in both directions
|
664
894
|
# and registers predicate relationships for each direction.
|
665
895
|
#
|
@@ -680,12 +910,15 @@ module ActiveFedora
|
|
680
910
|
def #{name}(opts={})
|
681
911
|
opts = {:rows=>25}.merge(opts)
|
682
912
|
if opts[:response_format] == :solr || opts[:response_format] == :load_from_solr
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
913
|
+
outbound_id_array = []
|
914
|
+
predicate = outbound_named_relationship_predicates["#{name}_outbound"]
|
915
|
+
if !outbound_relationships[predicate].nil?
|
916
|
+
outbound_relationships[predicate].each do |rel|
|
917
|
+
outbound_id_array << rel.gsub("info:fedora/", "")
|
918
|
+
end
|
919
|
+
end
|
920
|
+
#outbound_id_array = #{outbound_method_name}(:response_format=>:id_array)
|
921
|
+
query = self.class.bidirectional_named_relationship_query(self.pid,"#{name}",outbound_id_array)
|
689
922
|
solr_result = SolrService.instance.conn.query(query, :rows=>opts[:rows])
|
690
923
|
|
691
924
|
if opts[:response_format] == :solr
|
@@ -706,6 +939,12 @@ module ActiveFedora
|
|
706
939
|
def #{name}_from_solr
|
707
940
|
#{name}(:response_format => :load_from_solr)
|
708
941
|
end
|
942
|
+
def #{name}_solr_docs
|
943
|
+
#{name}(:response_format => :solr)
|
944
|
+
end
|
945
|
+
def #{name}_query
|
946
|
+
named_relationship_query("#{name}")
|
947
|
+
end
|
709
948
|
END
|
710
949
|
end
|
711
950
|
|
@@ -1,13 +1,13 @@
|
|
1
1
|
development:
|
2
2
|
fedora:
|
3
|
-
url: http://fedoraAdmin:fedoraAdmin@
|
3
|
+
url: http://fedoraAdmin:fedoraAdmin@localhost:8983/fedora
|
4
4
|
solr:
|
5
|
-
url: http://
|
5
|
+
url: http://localhost:8983/solr/development
|
6
6
|
test:
|
7
7
|
fedora:
|
8
|
-
url: http://fedoraAdmin:fedoraAdmin@
|
8
|
+
url: http://fedoraAdmin:fedoraAdmin@localhost:8983/fedora
|
9
9
|
solr:
|
10
|
-
url: http://
|
10
|
+
url: http://localhost:8983/solr/development
|
11
11
|
production:
|
12
12
|
fedora:
|
13
13
|
url: http://fedoraAdmin:fedoraAdmin@testhost.com:8080/fedora
|
@@ -20,7 +20,8 @@ describe ActiveFedora::NokogiriDatastream do
|
|
20
20
|
m.field 'collection', :string
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
|
+
RAILS_ENV='development'
|
24
25
|
@pid = "hydrangea:fixture_mods_article1"
|
25
26
|
@test_solr_object = HydrangeaArticle2.load_instance_from_solr(@pid)
|
26
27
|
@test_object = HydrangeaArticle2.load_instance(@pid)
|
@@ -13,6 +13,15 @@ describe ActiveFedora::SemanticNode do
|
|
13
13
|
class SNSpecModel < ActiveFedora::Base
|
14
14
|
has_relationship("parts", :is_part_of, :inbound => true)
|
15
15
|
has_relationship("containers", :is_member_of)
|
16
|
+
has_bidirectional_relationship("bi_containers", :is_member_of, :has_member)
|
17
|
+
end
|
18
|
+
class SpecNodeQueryParam < ActiveFedora::Base
|
19
|
+
has_relationship("parts", :is_part_of, :inbound => true)
|
20
|
+
has_relationship("special_parts", :is_part_of, :inbound => true, :query_params=>{:q=>{"has_model_s"=>"info:fedora/SpecialPart"}})
|
21
|
+
has_relationship("containers", :is_member_of)
|
22
|
+
has_relationship("special_containers", :is_member_of, :query_params=>{:q=>{"has_model_s"=>"info:fedora/SpecialContainer"}})
|
23
|
+
has_bidirectional_relationship("bi_containers", :is_member_of, :has_member)
|
24
|
+
has_bidirectional_relationship("bi_special_containers", :is_member_of, :has_member, :query_params=>{:q=>{"has_model_s"=>"info:fedora/SpecialContainer"}})
|
16
25
|
end
|
17
26
|
|
18
27
|
@test_object = SNSpecModel.new
|
@@ -25,24 +34,89 @@ describe ActiveFedora::SemanticNode do
|
|
25
34
|
@part2.add_relationship(:is_part_of, @test_object)
|
26
35
|
@part2.save
|
27
36
|
|
28
|
-
|
29
37
|
@container1 = ActiveFedora::Base.new()
|
30
38
|
@container1.save
|
31
39
|
@container2 = ActiveFedora::Base.new()
|
32
40
|
@container2.save
|
41
|
+
@container3 = ActiveFedora::Base.new()
|
42
|
+
@container3.save
|
33
43
|
|
34
44
|
@test_object.add_relationship(:is_member_of, @container1)
|
35
45
|
@test_object.add_relationship(:is_member_of, @container2)
|
36
46
|
@test_object.save
|
47
|
+
|
48
|
+
@special_container = ActiveFedora::Base.new()
|
49
|
+
@special_container.add_relationship(:has_model,"SpecialContainer")
|
50
|
+
@special_container.save
|
51
|
+
|
52
|
+
#even though adding container3 and special container, it should only include special_container when returning via named finder methods
|
53
|
+
#also should only return special part similarly
|
54
|
+
@test_object_query = SpecNodeQueryParam.new
|
55
|
+
@test_object_query.add_relationship(:is_member_of, @container3)
|
56
|
+
@test_object_query.add_relationship(:is_member_of, @special_container)
|
57
|
+
@test_object_query.save
|
58
|
+
|
59
|
+
@special_container2 = ActiveFedora::Base.new()
|
60
|
+
@special_container2.add_relationship(:has_model,"SpecialContainer")
|
61
|
+
@special_container2.add_relationship(:has_member,@test_object_query.pid)
|
62
|
+
@special_container2.save
|
63
|
+
|
64
|
+
@part3 = ActiveFedora::Base.new()
|
65
|
+
@part3.add_relationship(:is_part_of, @test_object_query)
|
66
|
+
@part3.save
|
67
|
+
|
68
|
+
@special_part = ActiveFedora::Base.new()
|
69
|
+
@special_part.add_relationship(:has_model,"SpecialPart")
|
70
|
+
@special_part.add_relationship(:is_part_of, @test_object_query)
|
71
|
+
@special_part.save
|
72
|
+
|
37
73
|
end
|
38
74
|
|
39
75
|
after(:all) do
|
76
|
+
begin
|
40
77
|
@part1.delete
|
78
|
+
rescue
|
79
|
+
end
|
80
|
+
begin
|
41
81
|
@part2.delete
|
82
|
+
rescue
|
83
|
+
end
|
84
|
+
begin
|
85
|
+
@part3.delete
|
86
|
+
rescue
|
87
|
+
end
|
88
|
+
begin
|
42
89
|
@container1.delete
|
90
|
+
rescue
|
91
|
+
end
|
92
|
+
begin
|
43
93
|
@container2.delete
|
94
|
+
rescue
|
95
|
+
end
|
96
|
+
begin
|
97
|
+
@container3.delete
|
98
|
+
rescue
|
99
|
+
end
|
100
|
+
begin
|
44
101
|
@test_object.delete
|
45
|
-
|
102
|
+
rescue
|
103
|
+
end
|
104
|
+
begin
|
105
|
+
@test_object_query.delete
|
106
|
+
rescue
|
107
|
+
end
|
108
|
+
begin
|
109
|
+
@special_part.delete
|
110
|
+
rescue
|
111
|
+
end
|
112
|
+
begin
|
113
|
+
@special_container.delete
|
114
|
+
rescue
|
115
|
+
end
|
116
|
+
begin
|
117
|
+
@special_container2.delete
|
118
|
+
rescue
|
119
|
+
end
|
46
120
|
Object.send(:remove_const, :SNSpecModel)
|
47
121
|
|
48
122
|
end
|
@@ -74,6 +148,73 @@ describe ActiveFedora::SemanticNode do
|
|
74
148
|
id.should satisfy {|id| id == @part1.pid || @part2.pid}
|
75
149
|
end
|
76
150
|
end
|
151
|
+
it "should return an array of Base objects with some filtered out if using query params" do
|
152
|
+
@test_object_query.special_parts_ids.should == [@special_part.pid]
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should return an array of all Base objects with relationship if not using query params" do
|
156
|
+
@test_object_query.parts_ids.size.should == 2
|
157
|
+
@test_object_query.parts_ids.include?(@special_part.pid).should == true
|
158
|
+
@test_object_query.parts_ids.include?(@part3.pid).should == true
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should return a solr query for an inbound relationship" do
|
162
|
+
@test_object_query.special_parts_query.should == "#{@test_object_query.named_relationship_predicates[:inbound]['special_parts']}_s:#{@test_object_query.internal_uri.gsub(/(:)/, '\\:')} AND has_model_s:info\\:fedora/SpecialPart"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe "inbound named relationship query" do
|
167
|
+
it "should return a properly formatted query for a relationship that has a query param defined" do
|
168
|
+
SpecNodeQueryParam.inbound_named_relationship_query(@test_object_query.pid,"special_parts").should == "#{@test_object_query.named_relationship_predicates[:inbound]['special_parts']}_s:#{@test_object_query.internal_uri.gsub(/(:)/, '\\:')} AND has_model_s:info\\:fedora/SpecialPart"
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should return a properly formatted query for a relationship that does not have a query param defined" do
|
172
|
+
SpecNodeQueryParam.inbound_named_relationship_query(@test_object_query.pid,"parts").should == "is_part_of_s:#{@test_object_query.internal_uri.gsub(/(:)/, '\\:')}"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "outbound named relationship query" do
|
177
|
+
it "should return a properly formatted query for a relationship that has a query param defined" do
|
178
|
+
expected_string = ""
|
179
|
+
@test_object_query.containers_ids.each_with_index do |id,index|
|
180
|
+
expected_string << " OR " if index > 0
|
181
|
+
expected_string << "(id:" + id.gsub(/(:)/, '\\:') + " AND has_model_s:info\\:fedora/SpecialContainer)"
|
182
|
+
end
|
183
|
+
SpecNodeQueryParam.outbound_named_relationship_query("special_containers",@test_object_query.containers_ids).should == expected_string
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should return a properly formatted query for a relationship that does not have a query param defined" do
|
187
|
+
expected_string = ""
|
188
|
+
@test_object_query.containers_ids.each_with_index do |id,index|
|
189
|
+
expected_string << " OR " if index > 0
|
190
|
+
expected_string << "id:" + id.gsub(/(:)/, '\\:')
|
191
|
+
end
|
192
|
+
SpecNodeQueryParam.outbound_named_relationship_query("containers",@test_object_query.containers_ids).should == expected_string
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "bidirectional named relationship query" do
|
197
|
+
it "should return a properly formatted query for a relationship that has a query param defined" do
|
198
|
+
expected_string = ""
|
199
|
+
@test_object_query.bi_containers_outbound_ids.each_with_index do |id,index|
|
200
|
+
expected_string << " OR " if index > 0
|
201
|
+
expected_string << "(id:" + id.gsub(/(:)/, '\\:') + " AND has_model_s:info\\:fedora/SpecialContainer)"
|
202
|
+
end
|
203
|
+
expected_string << " OR "
|
204
|
+
expected_string << "(#{@test_object_query.named_relationship_predicates[:inbound]['bi_special_containers_inbound']}_s:#{@test_object_query.internal_uri.gsub(/(:)/, '\\:')} AND has_model_s:info\\:fedora/SpecialContainer)"
|
205
|
+
SpecNodeQueryParam.bidirectional_named_relationship_query(@test_object_query.pid,"bi_special_containers",@test_object_query.bi_containers_outbound_ids).should == expected_string
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should return a properly formatted query for a relationship that does not have a query param defined" do
|
209
|
+
expected_string = ""
|
210
|
+
@test_object_query.bi_containers_outbound_ids.each_with_index do |id,index|
|
211
|
+
expected_string << " OR " if index > 0
|
212
|
+
expected_string << "id:" + id.gsub(/(:)/, '\\:')
|
213
|
+
end
|
214
|
+
expected_string << " OR "
|
215
|
+
expected_string << "(#{@test_object_query.named_relationship_predicates[:inbound]['bi_special_containers_inbound']}_s:#{@test_object_query.internal_uri.gsub(/(:)/, '\\:')})"
|
216
|
+
SpecNodeQueryParam.bidirectional_named_relationship_query(@test_object_query.pid,"bi_containers",@test_object_query.bi_containers_outbound_ids).should == expected_string
|
217
|
+
end
|
77
218
|
end
|
78
219
|
|
79
220
|
describe "outbound relationship finders" do
|
@@ -90,5 +231,121 @@ describe ActiveFedora::SemanticNode do
|
|
90
231
|
id.should satisfy {|id| id == @container1.pid || @container2.pid}
|
91
232
|
end
|
92
233
|
end
|
234
|
+
|
235
|
+
it "should return an array of Base objects with some filtered out if using query params" do
|
236
|
+
@test_object_query.special_containers_ids.size.should == 1
|
237
|
+
@test_object_query.special_containers_ids.include?(@container3.pid).should == false
|
238
|
+
@test_object_query.special_containers_ids.include?(@special_container.pid).should == true
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should return an array of all Base objects with relationship if not using query params" do
|
242
|
+
@test_object_query.containers_ids.size.should == 2
|
243
|
+
@test_object_query.containers_ids.include?(@special_container2.pid).should == false
|
244
|
+
@test_object_query.containers_ids.include?(@special_container.pid).should == true
|
245
|
+
@test_object_query.containers_ids.include?(@container3.pid).should == true
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should return a solr query for an outbound relationship" do
|
249
|
+
expected_string = ""
|
250
|
+
@test_object_query.containers_ids.each_with_index do |id,index|
|
251
|
+
expected_string << " OR " if index > 0
|
252
|
+
expected_string << "(id:" + id.gsub(/(:)/, '\\:') + " AND has_model_s:info\\:fedora/SpecialContainer)"
|
253
|
+
end
|
254
|
+
@test_object_query.special_containers_query.should == expected_string
|
255
|
+
end
|
93
256
|
end
|
257
|
+
|
258
|
+
describe "bidirectional relationship finders" do
|
259
|
+
it "should return an array of Base objects" do
|
260
|
+
containers = @test_object.bi_containers
|
261
|
+
containers.length.should > 0
|
262
|
+
containers.each do |container|
|
263
|
+
container.should be_kind_of(ActiveFedora::Base)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
it "_ids should return an array of pids" do
|
267
|
+
ids = @test_object.bi_containers_ids
|
268
|
+
ids.size.should == 2
|
269
|
+
ids.each do |id|
|
270
|
+
id.should satisfy {|id| id == @container1.pid || @container2.pid}
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
it "should return an array of Base objects with some filtered out if using query params" do
|
275
|
+
ids = @test_object_query.bi_special_containers_ids
|
276
|
+
ids.size.should == 2
|
277
|
+
ids.each do |id|
|
278
|
+
id.should satisfy {|id| id == @special_container.pid || @special_container2.pid}
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should return an array of all Base objects with relationship if not using query params" do
|
283
|
+
ids = @test_object_query.bi_containers_ids
|
284
|
+
ids.size.should == 3
|
285
|
+
ids.each do |id|
|
286
|
+
id.should satisfy {|id| id == @special_container.pid || @special_container2.pid || @container3.pid}
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should return a solr query for a bidirectional relationship" do
|
291
|
+
expected_string = ""
|
292
|
+
@test_object_query.bi_containers_outbound_ids.each_with_index do |id,index|
|
293
|
+
expected_string << " OR " if index > 0
|
294
|
+
expected_string << "(id:" + id.gsub(/(:)/, '\\:') + " AND has_model_s:info\\:fedora/SpecialContainer)"
|
295
|
+
end
|
296
|
+
expected_string << " OR "
|
297
|
+
expected_string << "(#{@test_object_query.named_relationship_predicates[:inbound]['bi_special_containers_inbound']}_s:#{@test_object_query.internal_uri.gsub(/(:)/, '\\:')} AND has_model_s:info\\:fedora/SpecialContainer)"
|
298
|
+
@test_object_query.bi_special_containers_query.should == expected_string
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
describe "named_relationship_query" do
|
303
|
+
it "should return correct query for a bidirectional relationship with query params" do
|
304
|
+
expected_string = ""
|
305
|
+
@test_object_query.bi_containers_outbound_ids.each_with_index do |id,index|
|
306
|
+
expected_string << " OR " if index > 0
|
307
|
+
expected_string << "(id:" + id.gsub(/(:)/, '\\:') + " AND has_model_s:info\\:fedora/SpecialContainer)"
|
308
|
+
end
|
309
|
+
expected_string << " OR "
|
310
|
+
expected_string << "(#{@test_object_query.named_relationship_predicates[:inbound]['bi_special_containers_inbound']}_s:#{@test_object_query.internal_uri.gsub(/(:)/, '\\:')} AND has_model_s:info\\:fedora/SpecialContainer)"
|
311
|
+
@test_object_query.named_relationship_query("bi_special_containers").should == expected_string
|
312
|
+
end
|
313
|
+
|
314
|
+
it "should return correct query for a bidirectional relationship without query params" do
|
315
|
+
expected_string = ""
|
316
|
+
@test_object_query.bi_containers_outbound_ids.each_with_index do |id,index|
|
317
|
+
expected_string << " OR " if index > 0
|
318
|
+
expected_string << "id:" + id.gsub(/(:)/, '\\:')
|
319
|
+
end
|
320
|
+
expected_string << " OR "
|
321
|
+
expected_string << "(#{@test_object_query.named_relationship_predicates[:inbound]['bi_special_containers_inbound']}_s:#{@test_object_query.internal_uri.gsub(/(:)/, '\\:')})"
|
322
|
+
@test_object_query.named_relationship_query("bi_containers").should == expected_string
|
323
|
+
end
|
324
|
+
|
325
|
+
it "should return a properly formatted query for an outbound relationship that has a query param defined" do
|
326
|
+
expected_string = ""
|
327
|
+
@test_object_query.containers_ids.each_with_index do |id,index|
|
328
|
+
expected_string << " OR " if index > 0
|
329
|
+
expected_string << "(id:" + id.gsub(/(:)/, '\\:') + " AND has_model_s:info\\:fedora/SpecialContainer)"
|
330
|
+
end
|
331
|
+
@test_object_query.named_relationship_query("special_containers").should == expected_string
|
332
|
+
end
|
333
|
+
|
334
|
+
it "should return a properly formatted query for an outbound relationship that does not have a query param defined" do
|
335
|
+
expected_string = ""
|
336
|
+
@test_object_query.containers_ids.each_with_index do |id,index|
|
337
|
+
expected_string << " OR " if index > 0
|
338
|
+
expected_string << "id:" + id.gsub(/(:)/, '\\:')
|
339
|
+
end
|
340
|
+
@test_object_query.named_relationship_query("containers").should == expected_string
|
341
|
+
end
|
342
|
+
|
343
|
+
it "should return a properly formatted query for an inbound relationship that has a query param defined" do
|
344
|
+
@test_object_query.named_relationship_query("special_parts").should == "#{@test_object_query.named_relationship_predicates[:inbound]['special_parts']}_s:#{@test_object_query.internal_uri.gsub(/(:)/, '\\:')} AND has_model_s:info\\:fedora/SpecialPart"
|
345
|
+
end
|
346
|
+
|
347
|
+
it "should return a properly formatted query for an inbound relationship that does not have a query param defined" do
|
348
|
+
@test_object_query.named_relationship_query("parts").should == "is_part_of_s:#{@test_object_query.internal_uri.gsub(/(:)/, '\\:')}"
|
349
|
+
end
|
350
|
+
end
|
94
351
|
end
|