author | Mahlon E. Smith <mahlon@martini.nu> |
Wed, 25 Sep 2019 16:06:51 -0700 | |
changeset 18 | e18bc5021028 |
parent 8 | 1ad0d5bc5083 |
child 20 | e2e96d97b77c |
permissions | -rw-r--r-- |
0 | 1 |
# -*- ruby -*- |
2 |
#encoding: utf-8 |
|
3 |
||
4 |
require 'sequel/model' |
|
5 |
||
6 |
require 'thingfish/mixins' |
|
7 |
require 'thingfish/metastore/pggraph' unless defined?( Thingfish::Metastore::PgGraph ) |
|
8 |
||
9 |
||
10 |
# A row of metadata describing an asset in a Thingfish store. |
|
11 |
class Thingfish::Metastore::PgGraph::Node < Sequel::Model( :nodes ) |
|
12 |
include Thingfish::Normalization |
|
13 |
||
14 |
# Related resources for this node |
|
15 |
one_to_many :related_nodes, :key => :id_p, :class => 'Thingfish::Metastore::PgGraph::Edge' |
|
16 |
||
17 |
# Edge relation if this node is a related resource |
|
18 |
one_to_one :related_to, :key => :id_c, :class => 'Thingfish::Metastore::PgGraph::Edge' |
|
19 |
||
20 |
# Allow instances to be created with a primary key |
|
21 |
unrestrict_primary_key |
|
22 |
||
23 |
||
24 |
# Dataset methods |
|
25 |
dataset_module do |
|
26 |
||
27 |
### Dataset method: Limit results to metadata which is for a related resource. |
|
28 |
### |
|
29 |
def related |
|
30 |
return self.join_edges( :rel ).exclude( :rel__id_c => nil ) |
|
31 |
end |
|
32 |
||
33 |
||
34 |
### Dataset method: Limit results to metadata which is not for a related resource. |
|
35 |
### |
|
36 |
def unrelated |
|
37 |
return self.join_edges( :notrel ).filter( :notrel__id_c => nil ) |
|
38 |
end |
|
39 |
||
40 |
||
41 |
### Dataset method: Limit results to records whose operational or user |
|
42 |
### metadata matches the values from the specified +hash+. |
|
43 |
### |
|
44 |
def where_metadata( hash ) |
|
45 |
ds = self |
|
46 |
hash.each do |field, value| |
|
47 |
||
48 |
# Direct DB column |
|
49 |
# |
|
50 |
if self.model.metadata_columns.include?( field.to_sym ) |
|
51 |
ds = ds.where( field.to_sym => value ) |
|
52 |
||
53 |
# User metadata or edge relationship |
|
54 |
# |
|
55 |
else |
|
56 |
if field.to_sym == :relationship |
|
8
1ad0d5bc5083
Fix criteria searches that include both 'relationship' and 'relation' fields.
Mahlon E. Smith <mahlon@martini.nu>
parents:
7
diff
changeset
|
57 |
ds = ds.join_edges unless ds.joined_dataset? |
1ad0d5bc5083
Fix criteria searches that include both 'relationship' and 'relation' fields.
Mahlon E. Smith <mahlon@martini.nu>
parents:
7
diff
changeset
|
58 |
ds = ds.filter( Sequel.pg_jsonb( :edges__prop ).get_text( field.to_s ) => value ) |
0 | 59 |
|
60 |
elsif field.to_sym == :relation |
|
8
1ad0d5bc5083
Fix criteria searches that include both 'relationship' and 'relation' fields.
Mahlon E. Smith <mahlon@martini.nu>
parents:
7
diff
changeset
|
61 |
ds = ds.join_edges unless ds.joined_dataset? |
0 | 62 |
ds = self.join_edges.filter( :edges__id_p => value ) |
63 |
||
64 |
else |
|
65 |
ds = ds.where( self.user_metadata_expr(field) => value ) |
|
66 |
end |
|
67 |
end |
|
68 |
end |
|
69 |
||
70 |
return ds |
|
71 |
end |
|
72 |
||
73 |
||
3
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
74 |
### Dataset method: Order results by the specified +columns+. |
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
75 |
### |
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
76 |
def order_metadata( *columns ) |
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
77 |
columns.flatten! |
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
78 |
ds = self |
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
79 |
columns.each do |column| |
7 | 80 |
if Thingfish::Metastore::PgGraph::Node.metadata_columns.include?( column.to_sym ) |
3
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
81 |
ds = ds.order_append( column.to_sym ) |
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
82 |
else |
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
83 |
ds = ds.order_append( self.user_metadata_expr(column) ) |
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
84 |
end |
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
85 |
end |
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
86 |
|
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
87 |
return ds |
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
88 |
end |
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
89 |
|
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
90 |
|
5b4ee98d698c
Pull recent Metastore::PG fixes into Metastore::PgGraph.
Mahlon E. Smith <mahlon@martini.nu>
parents:
0
diff
changeset
|
91 |
|
0 | 92 |
######### |
93 |
protected |
|
94 |
######### |
|
95 |
||
96 |
### Return a dataset linking related nodes to edges. |
|
97 |
### |
|
98 |
def join_edges( aka=nil ) |
|
99 |
return self.join_table( :left, :edges, { :id_c => :nodes__id }, { :table_alias => aka } ) |
|
100 |
end |
|
101 |
||
102 |
||
103 |
### Returns a Sequel expression suitable for use as the key of a query against |
|
104 |
### the specified user metadata field. |
|
105 |
def user_metadata_expr( field ) |
|
106 |
return Sequel.pg_jsonb( :user_metadata ).get_text( field.to_s ) |
|
107 |
end |
|
108 |
||
109 |
end # dataset_module |
|
110 |
||
111 |
||
112 |
### Return a new Metadata object from the given +oid+ and one-dimensional +hash+ |
|
113 |
### used by Thingfish. |
|
114 |
def self::from_hash( hash ) |
|
115 |
metadata = Thingfish::Normalization.normalize_keys( hash ) |
|
116 |
||
117 |
md = new |
|
118 |
||
119 |
md.format = metadata.delete( 'format' ) |
|
120 |
md.extent = metadata.delete( 'extent' ) |
|
121 |
md.created = metadata.delete( 'created' ) |
|
122 |
md.uploadaddress = metadata.delete( 'uploadaddress' ).to_s |
|
123 |
||
124 |
md.user_metadata = Sequel.pg_jsonb( metadata ) |
|
125 |
||
126 |
return md |
|
127 |
end |
|
128 |
||
129 |
||
130 |
### Return the columns of the table that are used for resource metadata. |
|
131 |
def self::metadata_columns |
|
132 |
return self.columns - [self.primary_key, :user_metadata] |
|
133 |
end |
|
134 |
||
135 |
||
136 |
### Do some initial attribute setup for new objects. |
|
137 |
def initialize( * ) |
|
138 |
super |
|
139 |
self[ :user_metadata ] ||= Sequel.pg_jsonb({}) |
|
140 |
end |
|
141 |
||
142 |
||
143 |
### Return the metadata as a Hash; overridden from Sequel::Model to |
|
144 |
### merge the user and system pairs together. |
|
145 |
def to_hash |
|
146 |
hash = self.values.dup |
|
147 |
||
148 |
hash.delete( :id ) |
|
149 |
hash.merge!( hash.delete(:user_metadata) ) |
|
150 |
||
151 |
if related_to = self.related_to |
|
152 |
hash.merge!( related_to.prop ) |
|
153 |
hash[ :relation ] = related_to.id_p |
|
154 |
end |
|
155 |
||
156 |
return normalize_keys( hash ) |
|
157 |
end |
|
158 |
||
159 |
||
160 |
### Merge new metadata +values+ into the metadata for the resource |
|
161 |
def merge!( values ) |
|
162 |
||
163 |
# Extract and set the column-metadata values first |
|
164 |
self.class.metadata_columns.each do |col| |
|
165 |
next unless values.key?( col.to_s ) |
|
166 |
self[ col ] = values.delete( col.to_s ) |
|
167 |
end |
|
168 |
||
169 |
self.user_metadata.merge!( values ) |
|
170 |
end |
|
171 |
||
172 |
||
173 |
### Hook creation for new related resources, divert relation data to |
|
174 |
### a new edge row. |
|
175 |
### |
|
176 |
def around_save |
|
177 |
relationship = self.user_metadata.delete( 'relationship' ) |
|
178 |
relation = self.user_metadata.delete( 'relation' ) |
|
179 |
||
180 |
super |
|
181 |
||
182 |
if relation |
|
183 |
edge = Thingfish::Metastore::PgGraph::Edge.new |
|
184 |
edge.prop[ 'relationship' ] = relationship |
|
185 |
edge.id_p = relation |
|
186 |
edge.id_c = self.id |
|
187 |
edge.save |
|
188 |
end |
|
189 |
end |
|
190 |
||
191 |
||
192 |
######### |
|
193 |
protected |
|
194 |
######### |
|
195 |
||
196 |
### Proxy method -- fetch a value from the metadata hash if it exists. |
|
197 |
def method_missing( sym, *args, &block ) |
|
198 |
return self.user_metadata[ sym.to_s ] || super |
|
199 |
end |
|
200 |
||
201 |
end # Thingfish::Metastore::PgGraph::Node |
|
202 |