class NoSE::Index
A representation of materialized views over fields in an entity
Attributes
Public Class Methods
# File lib/nose/indexes.rb, line 10 def initialize(hash_fields, order_fields, extra, graph, saved_key: nil) order_set = order_fields.to_set @hash_fields = hash_fields.to_set @order_fields = order_fields.delete_if { |e| hash_fields.include? e } @extra = extra.to_set.delete_if do |e| @hash_fields.include?(e) || order_set.include?(e) end @all_fields = Set.new(@hash_fields).merge(order_set).merge(@extra) validate_hash_fields # Store whether this index is an identity @identity = @hash_fields == [ @hash_fields.first.parent.id_field ].to_set && graph.nodes.size == 1 @graph = graph @path = graph.longest_path @path = nil unless @path.length == graph.size validate_graph build_hash saved_key end
Public Instance Methods
Two indices are equal if they contain the same fields @return [Boolean]
# File lib/nose/indexes.rb, line 88 def ==(other) hash == other.hash end
Look up a field in the index based on its ID @return [Fields::Field]
# File lib/nose/indexes.rb, line 50 def [](field_id) @all_fields.find { |field| field.id == field_id } end
Check if the index contains a given field @return [Boolean]
# File lib/nose/indexes.rb, line 110 def contains_field?(field) @all_fields.include? field end
# File lib/nose/indexes.rb, line 104 def hash @hash ||= Zlib.crc32 hash_str end
Hash based on the fields, their keys, and the graph @return [String]
# File lib/nose/indexes.rb, line 95 def hash_str @hash_str ||= [ @hash_fields.map(&:id).sort!, @order_fields.map(&:id), @extra.map(&:id).sort!, @graph.unique_edges.map(&:canonical_params).sort! ].to_s.freeze end
Check if this index is an ID graph @return [Boolean]
# File lib/nose/indexes.rb, line 56 def id_graph? @hash_fields.all?(&:primary_key?) && @order_fields.all?(&:primary_key) end
Check if this index maps from the primary key to fields from one entity @return [Boolean]
# File lib/nose/indexes.rb, line 38 def identity? @identity end
A simple key which uniquely identifies the index @return [String]
# File lib/nose/indexes.rb, line 44 def key @key ||= "i#{Zlib.crc32 hash_str}" end
:nocov:
# File lib/nose/indexes.rb, line 75 def to_color fields = [@hash_fields, @order_fields, @extra].map do |field_group| '[' + field_group.map(&:inspect).join(', ') + ']' end "[magenta]#{key}[/] #{fields[0]} #{fields[1]} → #{fields[2]}" \ " [yellow]$#{size}[/]" \ " [magenta]#{@graph.inspect}[/]" end
Produce an index with the same fields but keyed by entities in the graph
# File lib/nose/indexes.rb, line 61 def to_id_graph return self if id_graph? all_ids = (@hash_fields.to_a + @order_fields + @extra.to_a) all_ids.map! { |f| f.parent.id_field }.uniq! hash_fields = [all_ids.first] order_fields = all_ids[1..-1] extra = @all_fields - hash_fields - order_fields Index.new hash_fields, order_fields, extra, @graph end
Private Instance Methods
Initialize the hash function and freeze ourselves @return [void]
# File lib/nose/indexes.rb, line 118 def build_hash(saved_key) @key = saved_key hash key calculate_size freeze end
Precalculate the size of the index @return [void]
# File lib/nose/indexes.rb, line 170 def calculate_size @hash_count = @hash_fields.product_by(&:cardinality) # XXX This only works if foreign keys span all possible keys # Take the maximum possible count at each join and multiply @entries = @graph.entities.map(&:count).max @per_hash_count = (@entries * 1.0 / @hash_count) @entry_size = @all_fields.sum_by(&:size) @size = @entries * @entry_size end
Ensure an index and its fields correspond to a valid graph @return [void]
# File lib/nose/indexes.rb, line 146 def validate_graph validate_graph_entities validate_graph_keys end
Ensure the graph of the index is valid @return [void]
# File lib/nose/indexes.rb, line 153 def validate_graph_entities entities = @all_fields.map(&:parent).to_set fail InvalidIndexException, 'graph entities do match index' \ unless entities == @graph.entities.to_set end
We must have the primary keys of the all entities in the graph @return [void]
# File lib/nose/indexes.rb, line 161 def validate_graph_keys fail InvalidIndexException, 'missing graph entity keys' \ unless @graph.entities.map(&:id_field).all? do |field| @hash_fields.include?(field) || @order_fields.include?(field) end end
Check for valid hash fields in an index @return [void]
# File lib/nose/indexes.rb, line 129 def validate_hash_fields fail InvalidIndexException, 'hash fields cannot be empty' \ if @hash_fields.empty? fail InvalidIndexException, 'hash fields can only involve one entity' \ if @hash_fields.map(&:parent).to_set.size > 1 end
Ensure an index is nonempty @return [void]
# File lib/nose/indexes.rb, line 139 def validate_nonempty fail InvalidIndexException, 'must have fields other than hash fields' \ if @order_fields.empty? && @extra.empty? end