solis 0.97.0 → 0.98.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f85c33684ee96790f112dc77b73fea1a959abc8bdf3a3964e41e4b45fe35245c
4
- data.tar.gz: f36f6e68d649e5d09c3e542765e44102714d27c6e7e86202e0380d8bc57311e7
3
+ metadata.gz: 5b5f53e6ee613f19d1de7527908da768b55623f51f45bdc4ae31ab3ba338a5cc
4
+ data.tar.gz: d1dfade779a59ad0c36422a20bcc1b8b21fc7c74b75774e1b69be9488050c9ef
5
5
  SHA512:
6
- metadata.gz: eafffc463e38e575c289e4f3014998111b280d4f3155f6a1dc0057845ec98e7cd2ad09ebe05b7f91dae0dd66da00a9f2160d4093799f62b6fd5508978df5d4a2
7
- data.tar.gz: b707d9ff9eab9c56cef17597a9e9694e9df28fcab8133857f726996a063d619f0ba9cfa84cd66be2d567829d5d421ae2b3be4afcfb57c565dadad039dedf9926
6
+ metadata.gz: ec93a8f1b15c02a1b2b5b52e4f85d0d3058c2528ad73d33d15944e5a3c5609bbfe9e2e83e8ab15efc41c1e98350745903b089cf3902cd2157a3a2aaaeb0f5657
7
+ data.tar.gz: fd75bb60d80282d6c285b0bb004cdb466a0fa3d6ce2de9960732bb201e5d068c6c4256e7cf560dbf1b67b86371706659f562bc72bb149826285f22cefac6ba84
@@ -0,0 +1,130 @@
1
+ class Solis::Query::ResultTransformer
2
+ def initialize(model)
3
+ @model = model
4
+ @type_mappings = load_type_mappings
5
+ @cardinality_map = build_cardinality_map
6
+ end
7
+
8
+ def transform(data)
9
+ items = data.key?('@graph') ? data['@graph'] : [data]
10
+ items.map { |item| transform_item(item) }
11
+ end
12
+
13
+ private
14
+
15
+ def transform_item(item)
16
+ clean_item = remove_json_ld_metadata(item)
17
+ cast_and_shape(clean_item)
18
+ end
19
+
20
+ def cast_and_shape(item)
21
+ item.each_with_object({}) do |(key, value), result|
22
+ value = cast_value(value)
23
+ result[key] = enforce_cardinality(key, value)
24
+ end
25
+ end
26
+
27
+ # def cast_and_shape(item)
28
+ # item.each_with_object({}) do |(key, value), result|
29
+ # value = cast_value(value)
30
+ # value = enforce_cardinality(key, value)
31
+ # value = transform_nested_entity(key, value)
32
+ # result[key] = value
33
+ # end
34
+ # end
35
+
36
+ def transform_nested_entity(property_key, value)
37
+ return value if @model.nil?
38
+
39
+ metadata = @cardinality_map[property_key]
40
+ return value if metadata.nil?
41
+
42
+ datatype = metadata[:datatype]
43
+ node = metadata[:node]
44
+
45
+ # Check if datatype points to another entity (has a node)
46
+ if node.is_a?(RDF::URI) && value.is_a?(Hash)
47
+ # Recursively transform nested entity if we have its model
48
+ # You'd need a way to resolve the node URI to the model class
49
+ nested_model = resolve_model_from_node(datatype)
50
+ Solis::Query::ResultTransformer.new(nested_model).transform(value) if nested_model
51
+ elsif value.is_a?(Array) && value.all?(Hash)
52
+ # Transform array of nested entities
53
+ value.map { |v| transform_nested_entity(property_key, v) }
54
+ else
55
+ value
56
+ end
57
+ end
58
+
59
+ def resolve_model_from_node(datatype_node)
60
+ @model.graph.shape_as_model(datatype_node.to_s)
61
+ end
62
+
63
+
64
+ def remove_json_ld_metadata(item)
65
+ item.reject { |key| key.start_with?('@') }
66
+ end
67
+
68
+ def cast_value(value)
69
+ case value
70
+ when Hash
71
+ handle_typed_value(value)
72
+ when Array
73
+ value.map { |v| cast_value(v) }
74
+ else
75
+ value
76
+ end
77
+ end
78
+
79
+ def handle_typed_value(value)
80
+ return value unless value.key?('@type') && value.key?('@value')
81
+
82
+ type = value['@type']
83
+ raw_value = value['@value']
84
+
85
+ cast_by_type(type, raw_value)
86
+ end
87
+
88
+ def cast_by_type(type, value)
89
+ caster = @type_mappings[type]
90
+ caster ? caster.call(value) : value
91
+ end
92
+
93
+ def enforce_cardinality(property_key, value)
94
+ return value if @model.nil?
95
+
96
+ metadata = @cardinality_map[property_key]
97
+ return value if metadata.nil?
98
+
99
+ maxcount = metadata[:maxcount]
100
+
101
+ # If maxcount is nil or > 1, ensure it's an array
102
+ if maxcount.nil? || maxcount > 1
103
+ value.is_a?(Array) ? value : [value]
104
+ # If maxcount is 0 or 1, ensure it's a single value
105
+ else
106
+ value.is_a?(Array) ? value.first : value
107
+ end
108
+ end
109
+
110
+ def build_cardinality_map
111
+ return {} if @model.nil? || @model.metadata.nil?
112
+
113
+ attributes = @model.metadata[:attributes] || {}
114
+
115
+ attributes.each_with_object({}) do |(property_name, property_metadata), map|
116
+ map[property_name.to_s] = property_metadata
117
+ end
118
+ end
119
+
120
+ def load_type_mappings
121
+ {
122
+ "http://www.w3.org/2001/XMLSchema#dateTime" => ->(v) { DateTime.parse(v) },
123
+ "http://www.w3.org/2001/XMLSchema#date" => ->(v) { Date.parse(v) },
124
+ "http://www.w3.org/2006/time#DateTimeInterval" => ->(v) { ISO8601::TimeInterval.parse(v) },
125
+ "http://www.w3.org/2001/XMLSchema#boolean" => ->(v) { v == "true" },
126
+ "http://www.w3.org/2001/XMLSchema#integer" => ->(v) { v.to_i },
127
+ "http://www.w3.org/2001/XMLSchema#decimal" => ->(v) { BigDecimal(v) }
128
+ }
129
+ end
130
+ end
@@ -1,115 +1,75 @@
1
1
  require 'solis/store/sparql/client'
2
+ require 'solis/query/result_transformer'
2
3
  require 'solis/config_file'
3
4
 
4
5
  class Solis::Query::Runner
5
- def self.run(entity, query, options = {})
6
- result = {}
7
- context = JSON.parse %(
8
- {
9
- "@context": {
10
- "@vocab": "#{graph_name}",
11
- "id": "@id"
12
- },
13
- "@type": "#{entity}",
14
- "@embed": "@always"
15
- }
16
- )
17
-
18
- c = Solis::Store::Sparql::Client.new(Solis::Options.instance.get[:sparql_endpoint], graph_name: graph_name)
19
- r = c.query(query, options)
20
- if r.is_a?(SPARQL::Client)
21
- g = RDF::Graph.new
22
- t = r.query('select * where{?s ?p ?o}')
23
- t.each do |s|
24
- g << [s.s, s.p, s.o]
25
- end
6
+ def self.run(entity, query, options = {})
7
+ sparql_client = Solis::Store::Sparql::Client.new(
8
+ Solis::Options.instance.get[:sparql_endpoint],
9
+ graph_name: graph_name
10
+ )
26
11
 
27
- framed = nil
28
- JSON::LD::API.fromRDF(g) do |e|
29
- framed = JSON::LD::API.frame(e, context)
30
- end
31
- result = sanitize_result(framed)
32
- else
33
- t = []
34
- r.each do |s|
35
- t << s.to_h
36
- end
37
- result = sanitize_result({'@graph' => t})
38
- end
39
- result
40
- rescue StandardError => e
41
- puts e.message
42
- raise e
43
- end
12
+ raw_result = sparql_client.query(query, options)
13
+ model = options[:model] || nil
44
14
 
45
- private
15
+ transform_result(raw_result, entity, model)
16
+ rescue StandardError => e
17
+ puts e.message
18
+ raise e
19
+ end
46
20
 
47
- def self.sanitize_result(framed)
48
- data = framed&.key?('@graph') ? framed['@graph'] : [framed]
21
+ private
49
22
 
50
- sanitatize_data_in_result(data)
23
+ def self.transform_result(raw_result, entity, model)
24
+ if raw_result.is_a?(SPARQL::Client)
25
+ frame_and_transform(raw_result, entity, model)
26
+ else
27
+ transform_select_results(raw_result, model)
51
28
  end
29
+ end
52
30
 
53
- def self.sanitatize_data_in_result(data)
54
- data.map do |d|
55
- d.delete_if { |e| e =~ /^@/ }
56
- if d.is_a?(Hash)
57
- new_d = {}
58
- d.each do |k, v|
59
- if v.is_a?(Hash)
60
- if v.key?('@type')
61
- type = v['@type']
62
- if v.key?('@value')
63
- value = v['@value']
64
- case type
65
- when "http://www.w3.org/2001/XMLSchema#dateTime"
66
- value = DateTime.parse(value)
67
- when "http://www.w3.org/2001/XMLSchema#date"
68
- value = Date.parse(value)
69
- when "http://www.w3.org/2006/time#DateTimeInterval"
70
- value = ISO8601::TimeInterval.parse(value)
71
- when "http://www.w3.org/2001/XMLSchema#boolean"
72
- value = value == "true"
73
- end
74
- v = value
75
- end
76
- v = sanitize_result(v) if v.is_a?(Hash)
77
- end
78
- if v.is_a?(Hash)
79
- new_d[k] = v.class.method_defined?(:value) ? v.value : sanitize_result(v)
80
- else
81
- new_d[k] = v.class.method_defined?(:value) ? v.value : v
82
- end
83
- elsif v.is_a?(Array) #todo: make recursive
84
- new_d[k] = []
85
- v.each do |vt|
86
- if vt.is_a?(Hash)
87
- if vt.key?('@value')
88
- new_d[k] << vt['@value']
89
- else
90
- new_d[k] << (vt.is_a?(String) ? vt : sanitize_result(vt))
91
- end
92
- else
93
- new_d[k] << (vt.is_a?(String) ? vt : sanitize_result(vt))
94
- end
95
- end
96
- new_d[k].flatten!
97
- else
98
- new_d[k] = v.class.method_defined?(:value) ? v.value : v
99
- end
100
- end
101
- d = new_d
102
- end
31
+ def self.frame_and_transform(sparql_result, entity, model)
32
+ graph = build_graph_from_result(sparql_result)
33
+ context = build_context(entity)
103
34
 
104
- d
105
- end
106
- rescue StandardError => e
107
- Solis::LOGGER.error(e.message)
108
- data
35
+ framed = nil
36
+ JSON::LD::API.fromRDF(graph) do |expanded|
37
+ framed = JSON::LD::API.frame(expanded, context)
109
38
  end
110
39
 
111
- def self.graph_name
112
- raise Solis::Error::NotFoundError, 'No graph name found' if Solis::Options.instance.get.key?(:graphs).nil?
113
- Solis::Options.instance.get.key?(:graphs) ? Solis::Options.instance.get[:graphs].select{|s| s['type'].eql?(:main)}&.first['name'] : ''
40
+ Solis::Query::ResultTransformer.new(model).transform(framed)
41
+ end
42
+
43
+ def self.transform_select_results(raw_result, model)
44
+ results = raw_result.map(&:to_h)
45
+ Solis::Query::ResultTransformer.new(model).transform({'@graph' => results})
46
+ end
47
+
48
+ def self.build_graph_from_result(sparql_result)
49
+ graph = RDF::Graph.new
50
+ sparql_result.query('select * where{?s ?p ?o}').each do |statement|
51
+ graph << [statement.s, statement.p, statement.o]
114
52
  end
53
+ graph
54
+ end
55
+
56
+ def self.build_context(entity)
57
+ JSON.parse(%(
58
+ {
59
+ "@context": {
60
+ "@vocab": "#{graph_name}",
61
+ "id": "@id"
62
+ },
63
+ "@type": "#{entity}",
64
+ "@embed": "@always"
65
+ }
66
+ ))
67
+ end
68
+
69
+ def self.graph_name
70
+ graphs = Solis::Options.instance.get[:graphs]
71
+ raise Solis::Error::NotFoundError, 'No graph name found' if graphs.nil?
72
+
73
+ graphs.find { |g| g['type'].eql?(:main) }&.fetch('name') || ''
74
+ end
115
75
  end
data/lib/solis/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Solis
2
- VERSION = "0.97.0"
2
+ VERSION = "0.98.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.97.0
4
+ version: 0.98.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mehmet Celik
@@ -299,6 +299,7 @@ files:
299
299
  - lib/solis/query.rb
300
300
  - lib/solis/query/construct.rb
301
301
  - lib/solis/query/filter.rb
302
+ - lib/solis/query/result_transformer.rb
302
303
  - lib/solis/query/run.rb
303
304
  - lib/solis/resource.rb
304
305
  - lib/solis/shape.rb