Skip to content

Reuse query objects and skip re-parsing parsed tag lists - #91

Merged
igor-alexandrov merged 1 commit into
masterfrom
claude/taggable-array-metka-comparison-558618
Aug 18, 2026
Merged

Reuse query objects and skip re-parsing parsed tag lists#91
igor-alexandrov merged 1 commit into
masterfrom
claude/taggable-array-metka-comparison-558618

Conversation

@igor-alexandrov

Copy link
Copy Markdown
Collaborator

Why

The benchmark suite showed Metka statistically tied with acts-as-taggable-array-on on querying — both emit identical SQL against the same GIN index — but Metka's Ruby-side relation building carried avoidable per-call work: a fresh QueryBuilder and TagsQuery on every tagged_with call despite both being stateless, and GenericParser rebuilding TagList input that is its own, already-parsed output.

What

  • QueryBuilder exposes a shared frozen .instance; the two TagsQuery strategies (:all/:any) are prebuilt in a frozen STRATEGIES constant. .new remains public, so direct construction (as in the unit tests) still works.
  • tagged_with uses the shared instance.
  • GenericParser#call returns a TagList argument as-is instead of re-parsing it.

Numbers

Micro-benchmark of relation construction (no DB round trip), before → after:

Operation Before After
parser.call(TagList) 0.71 μs 0.05 μs
tagged_with(...).to_sql ~50 μs ~48.5 μs

Behavior note

A hand-built TagList containing empty strings is no longer filtered on re-parse. Metka itself never produces such a list, so no gem path or test is affected.

Tests

Full suite: 116 runs, 311 assertions, 0 failures, 0 errors.

🤖 Generated with Claude Code

Every tagged_with call built a fresh QueryBuilder and TagsQuery even
though both are stateless, and GenericParser rebuilt a TagList it had
itself produced. All of that work has the same result on every call, so
it was pure per-query allocation overhead.

QueryBuilder now exposes a shared frozen instance with the two TagsQuery
strategies (all/any) prebuilt, and the parser returns TagList input
as-is. Parsing an already-parsed list drops from ~0.7us to ~0.05us;
relation building gets ~2-4% cheaper. Behavior is unchanged except that
a hand-built TagList containing empty strings is no longer filtered —
Metka itself never produces one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@igor-alexandrov
igor-alexandrov merged commit e076492 into master Aug 18, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant