module Squint
Squint
json, jsonb, hstore queries
Constants
- HASH_DATA_COLUMNS
- VERSION
Public Class Methods
squint_hash_field_reln(*args)
click to toggle source
squint_hash_field_reln
return an Arel object with the appropriate query Strings want to be a SQL Literal, other things can be passed in bare to the eq or in operator
# File lib/squint.rb, line 91 def self.squint_hash_field_reln(*args) temp_attr = args[0] contains_nil = false column_type = self::HASH_DATA_COLUMNS[args[0].keys.first] column_name_segments = [] quote_char = '"'.freeze while temp_attr.is_a?(Hash) attribute_sym = temp_attr.keys.first.to_sym column_name_segments << (quote_char + temp_attr.keys.first.to_s + quote_char) quote_char = '\''.freeze temp_attr = temp_attr[temp_attr.keys.first] end check_attr_missing = squint_storext_default?(temp_attr, attribute_sym) # Check for nil in array if temp_attr.is_a? Array contains_nil = temp_attr.include?(nil) # remove the nil from the array - we'll handle that later temp_attr.compact! # if the Array is now just 1 element, it doesn't need to be # an Array any longer temp_attr = temp_attr[0] if temp_attr.size == 1 end if temp_attr.is_a? Array temp_attr = temp_attr.map(&:to_s) elsif ![FalseClass, TrueClass, NilClass].include?(temp_attr.class) temp_attr = temp_attr.to_s end query_value = if [Array, NilClass].include?(temp_attr.class) temp_attr else # strings or string-like things Arel::Nodes::Quoted.new(temp_attr.to_s) end # column_name_segments[0] = column_name_segments[0] attribute_selector = column_name_segments.join('->'.freeze) # JSON(B) data needs to have the last accessor be ->> instead of # -> . The ->> returns the data as text instead of jsonb. # hstore columns generally don't have nested keys / hashes # Possibly need to raise an error if the hash for an hstore # column references nested arrays? unless column_type == 'hstore'.freeze attribute_selector[attribute_selector.rindex('>'.freeze)] = '>>'.freeze end reln = if query_value.is_a?(Array) arel_table[Arel::Nodes::SqlLiteral.new(attribute_selector)].in(query_value) else arel_table[Arel::Nodes::SqlLiteral.new(attribute_selector)].eq(query_value) end # If a nil is present in an Array, need add a specific IS NULL comparison if contains_nil reln = Arel::Nodes::Grouping.new( reln.or(arel_table[Arel::Nodes::SqlLiteral.new(attribute_selector)].eq(nil)) ) end # check_attr_missing for StoreXT attributes where the default is # specified as a query value if check_attr_missing reln = if column_type == 'hstore'.freeze squint_hstore_element_missing(column_name_segments, reln) else squint_jsonb_element_missing(column_name_segments, reln) end end reln end
squint_hstore_element_exists(element, attribute_hash_column, value)
click to toggle source
# File lib/squint.rb, line 176 def self.squint_hstore_element_exists(element, attribute_hash_column, value) Arel::Nodes::Equality.new( Arel::Nodes::NamedFunction.new( "exist", [arel_table[Arel::Nodes::SqlLiteral.new(attribute_hash_column)], Arel::Nodes::SqlLiteral.new(element)] ), value ) end
squint_hstore_element_missing(column_name_segments, reln)
click to toggle source
# File lib/squint.rb, line 186 def self.squint_hstore_element_missing(column_name_segments, reln) element = column_name_segments.pop attribute_hash_column = column_name_segments.join('->'.freeze) # Query generated is equals default or attribute present is null or equals false # * Is null happens the the column is null # * equals false is when the column has jsonb data, but the key doesn't exist # ("posts"."storext_attributes"->>'is_awesome' = 'false' OR # (exists("posts"."storext_attributes", 'is_awesome') IS NULL OR # exists("posts"."storext_attributes", 'is_awesome') = FALSE) # ) Arel::Nodes::Grouping.new( reln.or( Arel::Nodes::Grouping.new( squint_hstore_element_exists(element, attribute_hash_column, Arel::Nodes::False.new) ).or( squint_hstore_element_exists(element, attribute_hash_column, nil) ) ) ) end
squint_jsonb_element_equality(element, attribute_hash_column, value)
click to toggle source
# File lib/squint.rb, line 207 def self.squint_jsonb_element_equality(element, attribute_hash_column, value) Arel::Nodes::Equality.new( Arel::Nodes::Grouping.new( Arel::Nodes::InfixOperation.new( Arel::Nodes::SqlLiteral.new('?'), arel_table[Arel::Nodes::SqlLiteral.new(attribute_hash_column)], Arel::Nodes::SqlLiteral.new(element) ) ), value ) end
squint_jsonb_element_missing(column_name_segments, reln)
click to toggle source
# File lib/squint.rb, line 219 def self.squint_jsonb_element_missing(column_name_segments, reln) element = column_name_segments.pop attribute_hash_column = column_name_segments.join('->'.freeze) # Query generated is equals default or attribute present is null or equals false # * Is null happens when the the whole column is null # * equals false is when the column has jsonb data, but the key doesn't exist # ("posts"."storext_attributes"->>'is_awesome' = 'false' OR # (("posts"."storext_attributes" ? 'is_awesome') IS NULL OR # ("posts"."storext_attributes" ? 'is_awesome') = FALSE) # ) Arel::Nodes::Grouping.new( reln.or( Arel::Nodes::Grouping.new( squint_jsonb_element_equality(element, attribute_hash_column, nil).or( squint_jsonb_element_equality(element, attribute_hash_column, Arel::Nodes::False.new) ) ) ) ) end
squint_storext_default?(temp_attr, attribute_sym)
click to toggle source
# File lib/squint.rb, line 164 def self.squint_storext_default?(temp_attr, attribute_sym) return false unless respond_to?(:storext_definitions) if storext_definitions.keys.include?(attribute_sym) && !(storext_definitions[attribute_sym][:opts] && storext_definitions[attribute_sym][:opts][:default]).nil? && [temp_attr].compact.map(&:to_s). flatten. include?(storext_definitions[attribute_sym][:opts][:default].to_s) true end end