lib/thingfish/metastore/pggraph/node.rb
changeset 0 3cc90e88c6ab
child 3 5b4ee98d698c
equal deleted inserted replaced
-1:000000000000 0:3cc90e88c6ab
       
     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
       
    57 						ds = self.join_edges.filter( Sequel.pg_jsonb( :edges__prop ).get_text( field.to_s ) => value )
       
    58 
       
    59 					elsif field.to_sym == :relation
       
    60 						ds = self.join_edges.filter( :edges__id_p => value )
       
    61 
       
    62 					else
       
    63 						ds = ds.where( self.user_metadata_expr(field) => value )
       
    64 					end
       
    65 				end
       
    66 			end
       
    67 
       
    68 			return ds
       
    69 		end
       
    70 
       
    71 
       
    72 		#########
       
    73 		protected
       
    74 		#########
       
    75 
       
    76 		### Return a dataset linking related nodes to edges.
       
    77 		###
       
    78 		def join_edges( aka=nil )
       
    79 			return self.join_table( :left, :edges, { :id_c => :nodes__id }, { :table_alias => aka } )
       
    80 		end
       
    81 
       
    82 
       
    83 		### Returns a Sequel expression suitable for use as the key of a query against
       
    84 		### the specified user metadata field.
       
    85 		def user_metadata_expr( field )
       
    86 			return Sequel.pg_jsonb( :user_metadata ).get_text( field.to_s )
       
    87 		end
       
    88 
       
    89 	end # dataset_module
       
    90 
       
    91 
       
    92 	### Return a new Metadata object from the given +oid+ and one-dimensional +hash+
       
    93 	### used by Thingfish.
       
    94 	def self::from_hash( hash )
       
    95 		metadata = Thingfish::Normalization.normalize_keys( hash )
       
    96 
       
    97 		md = new
       
    98 
       
    99 		md.format        = metadata.delete( 'format' )
       
   100 		md.extent        = metadata.delete( 'extent' )
       
   101 		md.created       = metadata.delete( 'created' )
       
   102 		md.uploadaddress = metadata.delete( 'uploadaddress' ).to_s
       
   103 
       
   104 		md.user_metadata = Sequel.pg_jsonb( metadata )
       
   105 
       
   106 		return md
       
   107 	end
       
   108 
       
   109 
       
   110 	### Return the columns of the table that are used for resource metadata.
       
   111 	def self::metadata_columns
       
   112 		return self.columns - [self.primary_key, :user_metadata]
       
   113 	end
       
   114 
       
   115 
       
   116 	### Do some initial attribute setup for new objects.
       
   117 	def initialize( * )
       
   118 		super
       
   119 		self[ :user_metadata ] ||= Sequel.pg_jsonb({})
       
   120 	end
       
   121 
       
   122 
       
   123 	### Return the metadata as a Hash; overridden from Sequel::Model to
       
   124 	### merge the user and system pairs together.
       
   125 	def to_hash
       
   126 		hash = self.values.dup
       
   127 
       
   128 		hash.delete( :id )
       
   129 		hash.merge!( hash.delete(:user_metadata) )
       
   130 
       
   131 		if related_to = self.related_to
       
   132 			hash.merge!( related_to.prop )
       
   133 			hash[ :relation ] = related_to.id_p
       
   134 		end
       
   135 
       
   136 		return normalize_keys( hash )
       
   137 	end
       
   138 
       
   139 
       
   140 	### Merge new metadata +values+ into the metadata for the resource
       
   141 	def merge!( values )
       
   142 
       
   143 		# Extract and set the column-metadata values first
       
   144 		self.class.metadata_columns.each do |col|
       
   145 			next unless values.key?( col.to_s )
       
   146 			self[ col ] = values.delete( col.to_s )
       
   147 		end
       
   148 
       
   149 		self.user_metadata.merge!( values )
       
   150 	end
       
   151 
       
   152 
       
   153 	### Hook creation for new related resources, divert relation data to
       
   154 	### a new edge row.
       
   155 	###
       
   156 	def around_save
       
   157 		relationship = self.user_metadata.delete( 'relationship' )
       
   158 		relation     = self.user_metadata.delete( 'relation' )
       
   159 
       
   160 		super
       
   161 
       
   162 		if relation
       
   163 			edge = Thingfish::Metastore::PgGraph::Edge.new
       
   164 			edge.prop[ 'relationship' ] = relationship
       
   165 			edge.id_p = relation
       
   166 			edge.id_c = self.id
       
   167 			edge.save
       
   168 		end
       
   169 	end
       
   170 
       
   171 
       
   172 	#########
       
   173 	protected
       
   174 	#########
       
   175 
       
   176 	### Proxy method -- fetch a value from the metadata hash if it exists.
       
   177 	def method_missing( sym, *args, &block )
       
   178 		return self.user_metadata[ sym.to_s ] || super
       
   179 	end
       
   180 
       
   181 end # Thingfish::Metastore::PgGraph::Node
       
   182