Class: UnnestedInFilters::Rewriter

Inherits:
Object
  • Object
show all
Includes:
Gitlab::Utils::StrongMemoize
Defined in:
lib/unnested_in_filters/rewriter.rb

Defined Under Namespace

Classes: ValueTable

Instance Method Summary collapse

Constructor Details

#initialize(relation) ⇒ Rewriter

Returns a new instance of Rewriter.



72
73
74
# File 'lib/unnested_in_filters/rewriter.rb', line 72

def initialize(relation)
  @relation = relation
end

Instance Method Details

#rewriteObject

Rewrites the given ActiveRecord::Relation object to utilize the DB indices efficiently.

Example usage;

relation = Vulnerabilities::Read.where(state: [1, 4])
relation = relation.order(severity: :desc, vulnerability_id: :desc)

rewriter = UnnestedInFilters::Rewriter.new(relation)
optimized_relation = rewriter.rewrite

In the above example. the ‘relation` object would produce the following SQL query;

SELECT
  "vulnerability_reads".*
FROM
  "vulnerability_reads"
WHERE
  "vulnerability_reads"."state" IN (1, 4)
ORDER BY
  "vulnerability_reads"."severity" DESC,
  "vulnerability_reads"."vulnerability_id" DESC
LIMIT 20;

And the ‘optimized_relation` object would would produce the following query to utilize the index on (state, severity, vulnerability_id);

SELECT
  "vulnerability_reads".*
FROM
  unnest('{1, 4}'::smallint[]) AS "states" ("state"),
  LATERAL (
    SELECT
      "vulnerability_reads".*
    FROM
      "vulnerability_reads"
    WHERE
      (vulnerability_reads."state" = "states"."state")
    ORDER BY
      "vulnerability_reads"."severity" DESC,
      "vulnerability_reads"."vulnerability_id" DESC
    LIMIT 20) AS vulnerability_reads
ORDER BY
  "vulnerability_reads"."severity" DESC,
  "vulnerability_reads"."vulnerability_id" DESC
LIMIT 20

If one of the columns being used for filtering or ordering is the primary key, then the query will be further optimized to use an index-only scan for initial filtering before selecting all columns using the primary key.

Using the prior query as an example, where ‘vulnerability_id` is the primary key, This will be rewritten to:

SELECT
  "vulnerability_reads".*
FROM
  "vulnerability_reads"
WHERE
  "vulnerability_reads"."vulnerability_id"
IN (
  SELECT
    "vulnerability_reads"."vulnerability_id"
  FROM
    unnest('{1, 4}'::smallint[]) AS "states" ("state"),
    LATERAL (
      SELECT
        "vulnerability_reads"."vulnerability_id"
      FROM
        "vulnerability_reads"
      WHERE
        (vulnerability_reads."state" = "states"."state")
      ORDER BY
        "vulnerability_reads"."severity" DESC,
        "vulnerability_reads"."vulnerability_id" DESC
        LIMIT 20
      ) AS vulnerability_reads
   )
ORDER BY
  "vulnerability_reads"."severity" DESC,
  "vulnerability_reads"."vulnerability_id" DESC
LIMIT 20


158
159
160
161
162
163
164
# File 'lib/unnested_in_filters/rewriter.rb', line 158

def rewrite
  log_rewrite

  return filter_query unless primary_key_present?

  index_only_filter_query
end

#rewrite?Boolean

Returns:

  • (Boolean)


166
167
168
169
170
# File 'lib/unnested_in_filters/rewriter.rb', line 166

def rewrite?
  strong_memoize(:rewrite) do
    in_filters.present? && has_index_coverage?
  end
end