valkyrie 0.1.0 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +5 -5
  2. data/.ctags +2 -0
  3. data/.rubocop.yml +5 -0
  4. data/.rubocop_todo.yml +3 -0
  5. data/CHANGELOG.md +43 -0
  6. data/Gemfile +0 -4
  7. data/LICENSE +15 -0
  8. data/README.md +13 -8
  9. data/Rakefile +15 -1
  10. data/bin/jetty_wait +14 -0
  11. data/bin/rspec +29 -0
  12. data/browserslist +3 -0
  13. data/circle.yml +17 -0
  14. data/config/fedora.yml +10 -0
  15. data/db/migrate/20161007101725_create_orm_resources.rb +9 -1
  16. data/db/migrate/20171011224121_create_path_gin_index.rb +6 -0
  17. data/db/migrate/20171204224121_create_internal_resource_index.rb +6 -0
  18. data/db/migrate/20180212092225_create_updated_at_index.rb +6 -0
  19. data/lib/generators/valkyrie/templates/resource_spec.rb.erb +1 -1
  20. data/lib/valkyrie.rb +1 -7
  21. data/lib/valkyrie/change_set.rb +21 -7
  22. data/lib/valkyrie/engine.rb +2 -0
  23. data/lib/valkyrie/id.rb +1 -0
  24. data/lib/valkyrie/indexers/access_controls_indexer.rb +50 -6
  25. data/lib/valkyrie/metadata_adapter.rb +29 -1
  26. data/lib/valkyrie/persistence.rb +27 -0
  27. data/lib/valkyrie/persistence/buffered_persister.rb +17 -1
  28. data/lib/valkyrie/persistence/composite_persister.rb +14 -2
  29. data/lib/valkyrie/persistence/custom_query_container.rb +63 -0
  30. data/lib/valkyrie/persistence/delete_tracking_buffer.rb +8 -0
  31. data/lib/valkyrie/persistence/fedora.rb +3 -0
  32. data/lib/valkyrie/persistence/fedora/list_node.rb +5 -2
  33. data/lib/valkyrie/persistence/fedora/metadata_adapter.rb +13 -11
  34. data/lib/valkyrie/persistence/fedora/ordered_list.rb +2 -2
  35. data/lib/valkyrie/persistence/fedora/ordered_reader.rb +3 -2
  36. data/lib/valkyrie/persistence/fedora/permissive_schema.rb +75 -0
  37. data/lib/valkyrie/persistence/fedora/persister.rb +22 -0
  38. data/lib/valkyrie/persistence/fedora/persister/model_converter.rb +110 -25
  39. data/lib/valkyrie/persistence/fedora/persister/orm_converter.rb +49 -17
  40. data/lib/valkyrie/persistence/fedora/persister/resource_factory.rb +2 -0
  41. data/lib/valkyrie/persistence/fedora/query_service.rb +46 -3
  42. data/lib/valkyrie/persistence/memory.rb +5 -0
  43. data/lib/valkyrie/persistence/memory/metadata_adapter.rb +6 -1
  44. data/lib/valkyrie/persistence/memory/persister.rb +19 -1
  45. data/lib/valkyrie/persistence/memory/query_service.rb +49 -8
  46. data/lib/valkyrie/persistence/postgres.rb +2 -0
  47. data/lib/valkyrie/persistence/postgres/metadata_adapter.rb +8 -1
  48. data/lib/valkyrie/persistence/postgres/orm.rb +1 -0
  49. data/lib/valkyrie/persistence/postgres/orm/resource.rb +12 -0
  50. data/lib/valkyrie/persistence/postgres/orm_converter.rb +99 -74
  51. data/lib/valkyrie/persistence/postgres/persister.rb +16 -0
  52. data/lib/valkyrie/persistence/postgres/query_service.rb +94 -6
  53. data/lib/valkyrie/persistence/postgres/resource_converter.rb +3 -1
  54. data/lib/valkyrie/persistence/postgres/resource_factory.rb +5 -5
  55. data/lib/valkyrie/persistence/solr.rb +2 -0
  56. data/lib/valkyrie/persistence/solr/composite_indexer.rb +29 -0
  57. data/lib/valkyrie/persistence/solr/metadata_adapter.rb +26 -1
  58. data/lib/valkyrie/persistence/solr/model_converter.rb +38 -8
  59. data/lib/valkyrie/persistence/solr/orm_converter.rb +43 -20
  60. data/lib/valkyrie/persistence/solr/persister.rb +16 -0
  61. data/lib/valkyrie/persistence/solr/queries.rb +3 -0
  62. data/lib/valkyrie/persistence/solr/queries/default_paginator.rb +2 -0
  63. data/lib/valkyrie/persistence/solr/queries/find_all_query.rb +3 -1
  64. data/lib/valkyrie/persistence/solr/queries/find_by_id_query.rb +4 -2
  65. data/lib/valkyrie/persistence/solr/queries/find_inverse_references_query.rb +2 -0
  66. data/lib/valkyrie/persistence/solr/queries/find_many_by_ids_query.rb +21 -0
  67. data/lib/valkyrie/persistence/solr/queries/find_members_query.rb +13 -5
  68. data/lib/valkyrie/persistence/solr/queries/find_references_query.rb +7 -3
  69. data/lib/valkyrie/persistence/solr/query_service.rb +30 -2
  70. data/lib/valkyrie/persistence/solr/repository.rb +14 -2
  71. data/lib/valkyrie/persistence/solr/resource_factory.rb +3 -1
  72. data/lib/valkyrie/resource.rb +11 -4
  73. data/lib/valkyrie/resource/access_controls.rb +13 -0
  74. data/lib/valkyrie/specs/shared_specs.rb +1 -2
  75. data/lib/valkyrie/specs/shared_specs/change_set.rb +75 -0
  76. data/lib/valkyrie/specs/shared_specs/metadata_adapter.rb +3 -0
  77. data/lib/valkyrie/specs/shared_specs/persister.rb +145 -15
  78. data/lib/valkyrie/specs/shared_specs/queries.rb +153 -27
  79. data/lib/valkyrie/specs/shared_specs/storage_adapter.rb +8 -3
  80. data/lib/valkyrie/storage.rb +29 -0
  81. data/lib/valkyrie/storage/disk.rb +17 -5
  82. data/lib/valkyrie/storage/fedora.rb +14 -1
  83. data/lib/valkyrie/storage/memory.rb +15 -2
  84. data/lib/valkyrie/storage_adapter.rb +26 -4
  85. data/lib/valkyrie/types.rb +65 -7
  86. data/lib/valkyrie/version.rb +1 -1
  87. data/solr/config/_rest_managed.json +3 -0
  88. data/solr/config/admin-extra.html +31 -0
  89. data/solr/config/elevate.xml +36 -0
  90. data/solr/config/mapping-ISOLatin1Accent.txt +246 -0
  91. data/solr/config/protwords.txt +21 -0
  92. data/solr/config/schema.xml +366 -0
  93. data/solr/config/scripts.conf +24 -0
  94. data/solr/config/solrconfig.xml +322 -0
  95. data/solr/config/spellings.txt +2 -0
  96. data/solr/config/stopwords.txt +58 -0
  97. data/solr/config/stopwords_en.txt +58 -0
  98. data/solr/config/synonyms.txt +31 -0
  99. data/solr/config/xslt/example.xsl +132 -0
  100. data/solr/config/xslt/example_atom.xsl +67 -0
  101. data/solr/config/xslt/example_rss.xsl +66 -0
  102. data/solr/config/xslt/luke.xsl +337 -0
  103. data/solr/solr.xml +35 -0
  104. data/tasks/dev.rake +66 -0
  105. data/valkyrie.gemspec +6 -6
  106. metadata +58 -63
  107. data/lib/valkyrie/decorators/decorator_list.rb +0 -15
  108. data/lib/valkyrie/decorators/decorator_with_arguments.rb +0 -14
  109. data/lib/valkyrie/derivative_service.rb +0 -42
  110. data/lib/valkyrie/file_characterization_service.rb +0 -42
  111. data/lib/valkyrie/local_file_service.rb +0 -11
  112. data/lib/valkyrie/persist_derivatives.rb +0 -29
  113. data/lib/valkyrie/persistence/postgres/queries.rb +0 -8
  114. data/lib/valkyrie/persistence/postgres/queries/find_inverse_references_query.rb +0 -31
  115. data/lib/valkyrie/persistence/postgres/queries/find_members_query.rb +0 -33
  116. data/lib/valkyrie/persistence/postgres/queries/find_references_query.rb +0 -33
  117. data/lib/valkyrie/specs/shared_specs/derivative_service.rb +0 -30
  118. data/lib/valkyrie/specs/shared_specs/file_characterization_service.rb +0 -33
@@ -2,6 +2,7 @@
2
2
  require 'valkyrie/persistence/postgres/orm'
3
3
  require 'valkyrie/persistence/postgres/resource_factory'
4
4
  module Valkyrie::Persistence::Postgres
5
+ # Persister for Postgres MetadataAdapter.
5
6
  class Persister
6
7
  attr_reader :adapter
7
8
  delegate :resource_factory, to: :adapter
@@ -11,6 +12,7 @@ module Valkyrie::Persistence::Postgres
11
12
 
12
13
  # (see Valkyrie::Persistence::Memory::Persister#save)
13
14
  def save(resource:)
15
+ ensure_multiple_values!(resource)
14
16
  orm_object = resource_factory.from_resource(resource: resource)
15
17
  orm_object.save!
16
18
  resource_factory.to_resource(object: orm_object)
@@ -29,5 +31,19 @@ module Valkyrie::Persistence::Postgres
29
31
  orm_object.delete
30
32
  resource
31
33
  end
34
+
35
+ # (see Valkyrie::Persistence::Memory::Persister#wipe!)
36
+ def wipe!
37
+ resource_factory.orm_class.delete_all
38
+ end
39
+
40
+ private
41
+
42
+ def ensure_multiple_values!(resource)
43
+ bad_keys = resource.attributes.except(:internal_resource, :created_at, :updated_at, :new_record, :id).select do |_k, v|
44
+ !v.nil? && !v.is_a?(Array)
45
+ end
46
+ raise ::Valkyrie::Persistence::UnsupportedDatatype, "#{resource}: #{bad_keys.keys} have non-array values, which can not be persisted by Valkyrie. Cast to arrays." unless bad_keys.keys.empty?
47
+ end
32
48
  end
33
49
  end
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
- require 'valkyrie/persistence/postgres/queries'
3
2
  module Valkyrie::Persistence::Postgres
3
+ # Query Service for the Postgres Metadata Adapter
4
+ #
5
+ # Most queries are delegated through to the ActiveRecord model
6
+ # {Valkyrie::Persistence::Postgres::ORM::Resource}
7
+ #
8
+ # @see Valkyrie::Persistence::Postgres::MetadataAdapter
4
9
  class QueryService
5
10
  attr_reader :adapter
6
11
  delegate :resource_factory, to: :adapter
@@ -25,14 +30,36 @@ module Valkyrie::Persistence::Postgres
25
30
 
26
31
  # (see Valkyrie::Persistence::Memory::QueryService#find_by)
27
32
  def find_by(id:)
28
- resource_factory.to_resource(object: orm_class.find(id))
33
+ id = Valkyrie::ID.new(id.to_s) if id.is_a?(String)
34
+ validate_id(id)
35
+ resource_factory.to_resource(object: orm_class.find(id.to_s))
36
+ rescue ActiveRecord::RecordNotFound
37
+ raise Valkyrie::Persistence::ObjectNotFoundError
38
+ end
39
+
40
+ # (see Valkyrie::Persistence::Memory::QueryService#find_many_by_ids)
41
+ def find_many_by_ids(ids:)
42
+ ids.map! do |id|
43
+ id = Valkyrie::ID.new(id.to_s) if id.is_a?(String)
44
+ validate_id(id)
45
+ id.to_s
46
+ end
47
+
48
+ orm_class.where(id: ids).map do |orm_resource|
49
+ resource_factory.to_resource(object: orm_resource)
50
+ end
29
51
  rescue ActiveRecord::RecordNotFound
30
52
  raise Valkyrie::Persistence::ObjectNotFoundError
31
53
  end
32
54
 
33
55
  # (see Valkyrie::Persistence::Memory::QueryService#find_members)
34
- def find_members(resource:)
35
- Valkyrie::Persistence::Postgres::Queries::FindMembersQuery.new(resource, resource_factory: resource_factory).run
56
+ def find_members(resource:, model: nil)
57
+ return [] if resource.id.blank?
58
+ if model
59
+ run_query(find_members_with_type_query, resource.id.to_s, model.to_s)
60
+ else
61
+ run_query(find_members_query, resource.id.to_s)
62
+ end
36
63
  end
37
64
 
38
65
  # (see Valkyrie::Persistence::Memory::QueryService#find_parents)
@@ -42,12 +69,73 @@ module Valkyrie::Persistence::Postgres
42
69
 
43
70
  # (see Valkyrie::Persistence::Memory::QueryService#find_references_by)
44
71
  def find_references_by(resource:, property:)
45
- Valkyrie::Persistence::Postgres::Queries::FindReferencesQuery.new(resource, property, resource_factory: resource_factory).run
72
+ return [] if resource.id.blank? || resource[property].blank?
73
+ run_query(find_references_query, property, resource.id.to_s)
46
74
  end
47
75
 
48
76
  # (see Valkyrie::Persistence::Memory::QueryService#find_inverse_references_by)
49
77
  def find_inverse_references_by(resource:, property:)
50
- Valkyrie::Persistence::Postgres::Queries::FindInverseReferencesQuery.new(resource, property, resource_factory: resource_factory).run
78
+ ensure_persisted(resource)
79
+ internal_array = "{\"#{property}\": [{\"id\": \"#{resource.id}\"}]}"
80
+ run_query(find_inverse_references_query, internal_array)
81
+ end
82
+
83
+ def run_query(query, *args)
84
+ orm_class.find_by_sql(([query] + args)).lazy.map do |object|
85
+ resource_factory.to_resource(object: object)
86
+ end
87
+ end
88
+
89
+ def find_members_query
90
+ <<-SQL
91
+ SELECT member.* FROM orm_resources a,
92
+ jsonb_array_elements(a.metadata->'member_ids') WITH ORDINALITY AS b(member, member_pos)
93
+ JOIN orm_resources member ON (b.member->>'id')::#{id_type} = member.id WHERE a.id = ?
94
+ ORDER BY b.member_pos
95
+ SQL
96
+ end
97
+
98
+ def find_members_with_type_query
99
+ <<-SQL
100
+ SELECT member.* FROM orm_resources a,
101
+ jsonb_array_elements(a.metadata->'member_ids') WITH ORDINALITY AS b(member, member_pos)
102
+ JOIN orm_resources member ON (b.member->>'id')::#{id_type} = member.id WHERE a.id = ?
103
+ AND member.internal_resource = ?
104
+ ORDER BY b.member_pos
105
+ SQL
106
+ end
107
+
108
+ def find_inverse_references_query
109
+ <<-SQL
110
+ SELECT * FROM orm_resources WHERE
111
+ metadata @> ?
112
+ SQL
113
+ end
114
+
115
+ def find_references_query
116
+ <<-SQL
117
+ SELECT member.* FROM orm_resources a,
118
+ jsonb_array_elements(a.metadata->?) AS b(member)
119
+ JOIN orm_resources member ON (b.member->>'id')::#{id_type} = member.id WHERE a.id = ?
120
+ SQL
121
+ end
122
+
123
+ def custom_queries
124
+ @custom_queries ||= ::Valkyrie::Persistence::CustomQueryContainer.new(query_service: self)
51
125
  end
126
+
127
+ private
128
+
129
+ def validate_id(id)
130
+ raise ArgumentError, 'id must be a Valkyrie::ID' unless id.is_a? Valkyrie::ID
131
+ end
132
+
133
+ def ensure_persisted(resource)
134
+ raise ArgumentError, 'resource is not saved' unless resource.persisted?
135
+ end
136
+
137
+ def id_type
138
+ @id_type ||= orm_class.columns_hash["id"].type
139
+ end
52
140
  end
53
141
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence::Postgres
3
+ # Responsible for converting a {Valkyrie::Resource} into a
4
+ # {Valkyrie::Persistence::Postgres::ORM::Resource}
3
5
  class ResourceConverter
4
6
  delegate :orm_class, to: :resource_factory
5
7
  attr_reader :resource, :resource_factory
@@ -9,7 +11,7 @@ module Valkyrie::Persistence::Postgres
9
11
  end
10
12
 
11
13
  def convert!
12
- orm_class.find_or_initialize_by(id: resource.id.to_s).tap do |orm_object|
14
+ orm_class.find_or_initialize_by(id: resource.id && resource.id.to_s).tap do |orm_object|
13
15
  orm_object.internal_resource = resource.internal_resource
14
16
  orm_object.metadata.merge!(resource.attributes.except(:id, :internal_resource, :created_at, :updated_at))
15
17
  end
@@ -2,9 +2,11 @@
2
2
  require 'valkyrie/persistence/postgres/orm_converter'
3
3
  require 'valkyrie/persistence/postgres/resource_converter'
4
4
  module Valkyrie::Persistence::Postgres
5
+ # Provides access to generic methods for converting to/from
6
+ # {Valkyrie::Resource} and {Valkyrie::Persistence::Postgres::ORM::Resource}.
5
7
  class ResourceFactory
6
8
  class << self
7
- # @param orm_object [Valkyrie::Persistence::Postgres::ORM::Resource] AR
9
+ # @param object [Valkyrie::Persistence::Postgres::ORM::Resource] AR
8
10
  # record to be converted.
9
11
  # @return [Valkyrie::Resource] Model representation of the AR record.
10
12
  def to_resource(object:)
@@ -16,12 +18,10 @@ module Valkyrie::Persistence::Postgres
16
18
  # resource for the Valkyrie resource.
17
19
  def from_resource(resource:)
18
20
  ::Valkyrie::Persistence::Postgres::ResourceConverter.new(resource, resource_factory: self).convert!
19
- # ::Valkyrie::Persistence::Postgres::ORM::Resource.find_or_initialize_by(id: resource.id.to_s).tap do |orm_object|
20
- # orm_object.internal_resource = resource.internal_resource
21
- # orm_object.metadata.merge!(resource.attributes.except(:id, :internal_resource, :created_at, :updated_at))
22
- # end
23
21
  end
24
22
 
23
+ # Accessor for the ActiveRecord class which all Postgres resources are an
24
+ # instance of.
25
25
  def orm_class
26
26
  ::Valkyrie::Persistence::Postgres::ORM::Resource
27
27
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence
3
+ # Implements the DataMapper Pattern to store metadata into Solr
3
4
  module Solr
4
5
  require 'valkyrie/persistence/solr/metadata_adapter'
6
+ require 'valkyrie/persistence/solr/composite_indexer'
5
7
  end
6
8
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie::Persistence::Solr
3
+ # Composite object to make multiple custom indexers act like a single one, so
4
+ # that upstream code doesn't have to know how to iterate over indexers.
5
+ #
6
+ # @see https://en.wikipedia.org/wiki/Composite_pattern
7
+ class CompositeIndexer
8
+ attr_reader :indexers
9
+ def initialize(*indexers)
10
+ @indexers = indexers
11
+ end
12
+
13
+ def new(resource:)
14
+ Instance.new(indexers, resource: resource)
15
+ end
16
+
17
+ class Instance
18
+ attr_reader :indexers, :resource
19
+ def initialize(indexers, resource:)
20
+ @resource = resource
21
+ @indexers = indexers.map { |i| i.new(resource: resource) }
22
+ end
23
+
24
+ def to_solr
25
+ indexers.map(&:to_solr).inject({}, &:merge)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -4,6 +4,28 @@ module Valkyrie::Persistence::Solr
4
4
  require 'valkyrie/persistence/solr/persister'
5
5
  require 'valkyrie/persistence/solr/query_service'
6
6
  require 'valkyrie/persistence/solr/resource_factory'
7
+ # MetadataAdapter for Solr Adapter.
8
+ #
9
+ # This adapter persists resources as hashes to an RSolr connection.
10
+ #
11
+ # @example Instantiate a Solr MetadataAdapter for Blacklight
12
+ # Valkyrie::Persistence::Solr::MetadataAdapter.new(
13
+ # connection: Blacklight.default_index.connection
14
+ # )
15
+ #
16
+ # @example Instantiate a Solr MetadataAdapter with just RSolr
17
+ # Valkyrie::Persistence::Solr::MetadataAdapter.new(
18
+ # connection: RSolr.connect(url: "http://127.0.0.1:8983/solr/core")
19
+ # )
20
+ #
21
+ # @example Instantiate a Solr MetadataAdapter with custom indexers
22
+ # Valkyrie::Persistence::Solr::MetadataAdapter.new(
23
+ # connection: Blacklight.default_index.connection,
24
+ # resource_indexer: CompositeIndexer.new(
25
+ # Valkyrie::Indexers::AccessControlsIndexer,
26
+ # MyIndexer
27
+ # )
28
+ # )
7
29
  class MetadataAdapter
8
30
  attr_reader :connection, :resource_indexer
9
31
  # @param connection [RSolr::Client] The RSolr connection to index to.
@@ -22,7 +44,10 @@ module Valkyrie::Persistence::Solr
22
44
  # @return [Valkyrie::Persistence::Solr::QueryService] The solr query
23
45
  # service.
24
46
  def query_service
25
- Valkyrie::Persistence::Solr::QueryService.new(connection: connection, resource_factory: resource_factory)
47
+ @query_service ||= Valkyrie::Persistence::Solr::QueryService.new(
48
+ connection: connection,
49
+ resource_factory: resource_factory
50
+ )
26
51
  end
27
52
 
28
53
  # @return [Valkyrie::Persistence::Solr::ResourceFactory] A resource factory
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence::Solr
3
+ # Responsible for converting a {Valkyrie::Resource} into hashes for indexing
4
+ # into Solr.
3
5
  class ModelConverter
4
6
  attr_reader :resource, :resource_factory
5
7
  delegate :resource_indexer, to: :resource_factory
@@ -9,7 +11,8 @@ module Valkyrie::Persistence::Solr
9
11
  end
10
12
 
11
13
  def convert!
12
- to_h.merge(internal_resource_ssim: [resource.internal_resource]).merge(indexer_solr(resource))
14
+ to_h.merge(Valkyrie::Persistence::Solr::Queries::MODEL.to_sym => [resource.internal_resource])
15
+ .merge(indexer_solr(resource))
13
16
  end
14
17
 
15
18
  def indexer_solr(resource)
@@ -18,19 +21,24 @@ module Valkyrie::Persistence::Solr
18
21
 
19
22
  # @return [String] The solr document ID
20
23
  def id
21
- "id-#{resource.id}"
24
+ resource.id.to_s
22
25
  end
23
26
 
24
27
  # @return [String] ISO-8601 timestamp in UTC of the created_at for this solr
25
28
  # document.
26
29
  def created_at
27
- resource_attributes[:created_at] || Time.current.utc.iso8601
30
+ if resource_attributes[:created_at]
31
+ DateTime.parse(resource_attributes[:created_at].to_s).utc.iso8601
32
+ else
33
+ Time.current.utc.iso8601
34
+ end
28
35
  end
29
36
 
30
37
  # @return [Hash] Solr document to index.
31
38
  def to_h
32
39
  {
33
40
  "id": id,
41
+ "join_id_ssi": "id-#{id}",
34
42
  "created_at_dtsi": created_at
35
43
  }.merge(attribute_hash)
36
44
  end
@@ -39,12 +47,18 @@ module Valkyrie::Persistence::Solr
39
47
 
40
48
  def attribute_hash
41
49
  properties.each_with_object({}) do |property, hsh|
42
- SolrMapperValue.for(Property.new(property, resource_attributes[property])).result.apply_to(hsh)
50
+ attr = resource_attributes[property]
51
+ mapper_val = SolrMapperValue.for(Property.new(property, attr)).result
52
+ unless mapper_val.respond_to?(:apply_to)
53
+ raise "Unable to cast #{resource_attributes[:internal_resource]}#" \
54
+ "#{property} which has been set to an instance of '#{attr.class}'"
55
+ end
56
+ mapper_val.apply_to(hsh)
43
57
  end
44
58
  end
45
59
 
46
60
  def properties
47
- resource_attributes.keys - [:id, :created_at, :updated_at]
61
+ resource_attributes.keys - [:id, :created_at, :updated_at, :new_record]
48
62
  end
49
63
 
50
64
  def resource_attributes
@@ -116,6 +130,18 @@ module Valkyrie::Persistence::Solr
116
130
  class SolrMapperValue < ::Valkyrie::ValueMapper
117
131
  end
118
132
 
133
+ # Casts {Boolean} values into a recognizable string in Solr.
134
+ class BooleanPropertyValue < ::Valkyrie::ValueMapper
135
+ SolrMapperValue.register(self)
136
+ def self.handles?(value)
137
+ value.is_a?(Property) && ([true, false].include? value.value)
138
+ end
139
+
140
+ def result
141
+ calling_mapper.for(Property.new(value.key, "boolean-#{value.value}")).result
142
+ end
143
+ end
144
+
119
145
  # Casts nested resources into a JSON string in solr.
120
146
  class NestedObjectValue < ::Valkyrie::ValueMapper
121
147
  SolrMapperValue.register(self)
@@ -224,7 +250,8 @@ module Valkyrie::Persistence::Solr
224
250
  CompositeSolrRow.new(
225
251
  [
226
252
  calling_mapper.for(Property.new(value.key, value.value)).result,
227
- calling_mapper.for(Property.new("#{value.key}_lang", "eng")).result
253
+ calling_mapper.for(Property.new("#{value.key}_lang", "eng")).result,
254
+ calling_mapper.for(Property.new("#{value.key}_type", "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString")).result
228
255
  ]
229
256
  )
230
257
  end
@@ -258,10 +285,13 @@ module Valkyrie::Persistence::Solr
258
285
  end
259
286
 
260
287
  def result
288
+ key = value.key
289
+ val = value.value
261
290
  CompositeSolrRow.new(
262
291
  [
263
- calling_mapper.for(Property.new(value.key, value.value.to_s)).result,
264
- calling_mapper.for(Property.new("#{value.key}_lang", value.value.language.to_s)).result
292
+ calling_mapper.for(Property.new(key, val.to_s)).result,
293
+ calling_mapper.for(Property.new("#{key}_lang", val.language.to_s)).result,
294
+ calling_mapper.for(Property.new("#{key}_type", val.datatype.to_s)).result
265
295
  ]
266
296
  )
267
297
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence::Solr
3
- ##
4
- # Converts a solr hash to a {Valkyrie::Resource}
3
+ # Responsible for converting hashes from Solr into a {Valkyrie::Resource}
5
4
  class ORMConverter
6
5
  attr_reader :solr_document
7
6
  def initialize(solr_document)
@@ -13,7 +12,7 @@ module Valkyrie::Persistence::Solr
13
12
  end
14
13
 
15
14
  def resource
16
- resource_klass.new(attributes.symbolize_keys)
15
+ resource_klass.new(attributes.symbolize_keys.merge(new_record: false))
17
16
  end
18
17
 
19
18
  def resource_klass
@@ -21,7 +20,7 @@ module Valkyrie::Persistence::Solr
21
20
  end
22
21
 
23
22
  def internal_resource
24
- solr_document["internal_resource_ssim"].first
23
+ solr_document.fetch(Valkyrie::Persistence::Solr::Queries::MODEL).first
25
24
  end
26
25
 
27
26
  def attributes
@@ -29,15 +28,15 @@ module Valkyrie::Persistence::Solr
29
28
  end
30
29
 
31
30
  def created_at
32
- DateTime.parse(solr_document["created_at_dtsi"].to_s).utc
31
+ DateTime.parse(solr_document.fetch("created_at_dtsi").to_s).utc
33
32
  end
34
33
 
35
34
  def updated_at
36
- DateTime.parse(solr_document["timestamp"] || solr_document["created_at_dtsi"].to_s).utc
35
+ DateTime.parse(solr_document["timestamp"] || solr_document.fetch("created_at_dtsi").to_s).utc
37
36
  end
38
37
 
39
38
  def id
40
- solr_document["id"].gsub(/^id-/, '')
39
+ solr_document.fetch('id').sub(/^id-/, '')
41
40
  end
42
41
 
43
42
  def attribute_hash
@@ -49,7 +48,7 @@ module Valkyrie::Persistence::Solr
49
48
  def strip_tsim(hsh)
50
49
  Hash[
51
50
  hsh.map do |k, v|
52
- [k.gsub("_tsim", ""), v]
51
+ [k.sub("_tsim", ""), v]
53
52
  end
54
53
  ]
55
54
  end
@@ -66,6 +65,7 @@ module Valkyrie::Persistence::Solr
66
65
  def build_literals(hsh)
67
66
  hsh.each_with_object({}) do |(key, value), output|
68
67
  next if key.end_with?("_lang")
68
+ next if key.end_with?("_type")
69
69
  output[key] = SolrValue.for(Property.new(key, value, hsh)).result
70
70
  end
71
71
  end
@@ -75,26 +75,36 @@ module Valkyrie::Persistence::Solr
75
75
 
76
76
  # Converts a stored language typed literal from two fields into one
77
77
  # {RDF::Literal}
78
- class LanguagePropertyValue < ::Valkyrie::ValueMapper
78
+ class RDFLiteralPropertyValue < ::Valkyrie::ValueMapper
79
79
  SolrValue.register(self)
80
80
  def self.handles?(value)
81
- value.is_a?(Property) && value.document["#{value.key}_lang"]
81
+ value.is_a?(Property) &&
82
+ (value.document["#{value.key}_lang"] || value.document["#{value.key}_type"])
82
83
  end
83
84
 
84
85
  def result
85
- value.value.zip(languages).map do |literal, language|
86
- if language == "eng"
86
+ value.value.each_with_index.map do |literal, idx|
87
+ language = languages[idx]
88
+ type = datatypes[idx]
89
+ if language == "eng" && type == "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString"
87
90
  literal
91
+ elsif language.present?
92
+ RDF::Literal.new(literal, language: language, datatype: type)
88
93
  else
89
- RDF::Literal.new(literal, language: language)
94
+ RDF::Literal.new(literal, datatype: type)
90
95
  end
91
96
  end
92
97
  end
93
98
 
94
99
  def languages
95
- value.document["#{value.key}_lang"]
100
+ value.document.fetch("#{value.key}_lang", [])
101
+ end
102
+
103
+ def datatypes
104
+ value.document.fetch("#{value.key}_type", [])
96
105
  end
97
106
  end
107
+
98
108
  class PropertyValue < ::Valkyrie::ValueMapper
99
109
  SolrValue.register(self)
100
110
  def self.handles?(value)
@@ -126,7 +136,7 @@ module Valkyrie::Persistence::Solr
126
136
  end
127
137
 
128
138
  def result
129
- Valkyrie::ID.new(value.gsub(/^id-/, ''))
139
+ Valkyrie::ID.new(value.sub(/^id-/, ''))
130
140
  end
131
141
  end
132
142
 
@@ -138,7 +148,7 @@ module Valkyrie::Persistence::Solr
138
148
  end
139
149
 
140
150
  def result
141
- ::RDF::URI.new(value.gsub(/^uri-/, ''))
151
+ ::RDF::URI.new(value.sub(/^uri-/, ''))
142
152
  end
143
153
  end
144
154
 
@@ -154,7 +164,7 @@ module Valkyrie::Persistence::Solr
154
164
  end
155
165
 
156
166
  def json
157
- value.gsub(/^serialized-/, '')
167
+ value.sub(/^serialized-/, '')
158
168
  end
159
169
  end
160
170
 
@@ -222,6 +232,19 @@ module Valkyrie::Persistence::Solr
222
232
  end
223
233
  end
224
234
 
235
+ # Converts an boolean in solr into an {Boolean}
236
+ class BooleanValue < ::Valkyrie::ValueMapper
237
+ SolrValue.register(self)
238
+ def self.handles?(value)
239
+ value.to_s.start_with?("boolean-")
240
+ end
241
+
242
+ def result
243
+ val = value.sub(/^boolean-/, '')
244
+ val.casecmp("true").zero?
245
+ end
246
+ end
247
+
225
248
  # Converts an integer in solr into an {Integer}
226
249
  class IntegerValue < ::Valkyrie::ValueMapper
227
250
  SolrValue.register(self)
@@ -230,7 +253,7 @@ module Valkyrie::Persistence::Solr
230
253
  end
231
254
 
232
255
  def result
233
- value.gsub(/^integer-/, '').to_i
256
+ value.sub(/^integer-/, '').to_i
234
257
  end
235
258
  end
236
259
 
@@ -239,13 +262,13 @@ module Valkyrie::Persistence::Solr
239
262
  SolrValue.register(self)
240
263
  def self.handles?(value)
241
264
  return false unless value.to_s.start_with?("datetime-")
242
- DateTime.iso8601(value.gsub(/^datetime-/, '')).utc
265
+ DateTime.iso8601(value.sub(/^datetime-/, '')).utc
243
266
  rescue
244
267
  false
245
268
  end
246
269
 
247
270
  def result
248
- DateTime.parse(value.gsub(/^datetime-/, '')).utc
271
+ DateTime.parse(value.sub(/^datetime-/, '')).utc
249
272
  end
250
273
  end
251
274
  end