lib/conceptql/query.rb in conceptql-0.2.0 vs lib/conceptql/query.rb in conceptql-0.3.0

- old
+ new

@@ -1,50 +1,96 @@ -require 'psych' +require 'json' +require 'open3' require 'forwardable' -require_relative 'behaviors/preppable' -require_relative 'tree' +require_relative 'scope' +require_relative 'nodifier' +require_relative 'sql_formatter' module ConceptQL class Query extend Forwardable - def_delegators :prepped_query, :all, :count, :execute, :order + def_delegators :query, :all, :count, :execute, :order, :profile + def_delegators :db, :profile_for attr :statement - def initialize(db, statement, tree = Tree.new) + def initialize(db, statement, opts={}) @db = db - @db.extend_datasets(ConceptQL::Behaviors::Preppable) - @statement = statement - @tree = tree + @statement = extract_statement(statement) + opts = opts.dup + opts[:algorithm_fetcher] ||= proc do |alg| + statement, description = db[:concepts].where(concept_id: alg).get([:statement, :label]) + statement = JSON.parse(statement) if statement.is_a?(String) + [statement, description] + end + @nodifier = opts[:nodifier] || Nodifier.new({ database_type: db.database_type}.merge(opts)) end def query - build_query(db) + nodifier.scope.with_ctes(operator.evaluate(db), db) end + def query_cols(opts = {}) + cols = operator.dynamic_columns + if opts[:cast] + cols = query_cols.each_with_object({}) do |column, h| + h[column] = operator.cast_column(column) + end + end + cols + end + def sql - (tree.scope.sql(db) << operator.sql(db)).join(";\n\n") + ';' + SqlFormatter.new.format(query.sql) + rescue + puts $!.message + puts $!.backtrace.join("\n") + return "SQL unavailable for this statement" end - def types - tree.root(self).types + def annotate(opts = {}) + operator.annotate(db, opts) end - def operator - @operator ||= tree.root(self) + def scope_annotate(opts = {}) + annotate(opts) + nodifier.scope.annotation end - private - attr :yaml, :tree, :db + def optimized + n = dup + n.instance_variable_set(:@operator, operator.optimized) + n + end - def build_query(db) - operator.evaluate(db).tap { |q| q.prep_proc = prep_proc } + def domains + operator.domains(db) end - def prep_proc - @prep_proc = Proc.new { puts 'PREPPING'; tree.scope.prep(db) } + def operator + @operator ||= if statement.is_a?(Array) + if statement.first.is_a?(Array) + Operators::Invalid.new(nodifier, "invalid", errors: [["incomplete statement"]]) + else + nodifier.create(*statement) + end + else + Operators::Invalid.new(nodifier, "invalid", errors: [["invalid root operator", statement.inspect]]) + end end - def prepped_query - query + def code_list(db) + operator.code_list(db) + end + + private + attr :db, :nodifier + + + def extract_statement(stmt) + if stmt.is_a?(Array) && stmt.length == 1 && stmt.first.is_a?(Array) + stmt.first + else + stmt + end end end end