Sha256: e6214fff3edbc063a08ec66fac7ec790aa817c7578eeba7fc9bf9fb5eac9e24b

Contents?: true

Size: 1.77 KB

Versions: 1

Compression:

Stored size: 1.77 KB

Contents

module Minidusen
  class Syntax

    def initialize
      @scopers = {}
      @alias_count = 0
    end

    def learn_field(field, &scoper)
      field = field.to_s
      @scopers[field] = scoper
    end

    def search(instance, root_scope, query)
      query = parse(query)
      query = query.condensed
      matches = apply_query(instance, root_scope, query.include)
      if query.exclude.any?
        matches = append_excludes(instance, matches, query.exclude)
      end
      matches
    end

    def fields
      @scopers
    end

    def parse(query)
      Parser.parse(query)
    end

    private

    NONE = lambda do |scope, *args|
      scope.where('1=2')
    end

    def apply_query(instance, root_scope, query)
      scope = root_scope
      query.each do |token|
        scoper = @scopers[token.field] || NONE
        scope = instance.instance_exec(scope, token.value, &scoper)
      end
      scope
    end

    def append_excludes(instance, matches, exclude_query)
      excluded_records = apply_query(instance, matches.origin_class, exclude_query)
      primary_key = excluded_records.primary_key
      join_alias = "exclude_#{@alias_count += 1}"
      # due to performance reasons on big tables this needs to be implemented as an anti-join
      # will generate SQL like
      # LEFT JOIN (SELECT "users"."id" FROM "users" WHERE $condition) excluded
      # ON "users"."id" = "excluded"."id"
      # WHERE "excluded"."id" IS NULL
      matches
        .joins(<<~SQL)
          LEFT JOIN (#{excluded_records.select(primary_key).to_sql}) #{join_alias}
          ON #{Util.qualify_column_name(excluded_records, primary_key)} = #{Util.qualify_column_name(excluded_records, primary_key, table_name: join_alias)}
        SQL
        .where(join_alias => { primary_key => nil })
    end

  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
minidusen-0.11.2 lib/minidusen/syntax.rb