Skip to content

feat: Distribute exact-match lucene variable references - #2987

Open
pulpdrew wants to merge 3 commits into
mainfrom
drew/lucene-variable-distribute
Open

feat: Distribute exact-match lucene variable references#2987
pulpdrew wants to merge 3 commits into
mainfrom
drew/lucene-variable-distribute

Conversation

@pulpdrew

@pulpdrew pulpdrew commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR enhances support for variables in lucene by rewriting Field:"$var" to (Field:"A" OR Field"B") (when A and B are selected for $var. This preserves exact-match semantics. The basic expansion Field:("A" OR "B") is a substring condition `Field ILIKE "%A%" OR Field ILIKE "%B%".

The quoted form was chosen for exact-match semantics because Field:"A" is exact match. Field:$var continues using the basic expansion, matching Field:A substring semantics.

How

The rewrite is done through the following process:

  1. Tokenize the lucene input string using the existing macro/variable tokenizer. This turns the text into a stream of Variable/Macro/Text tokens.
  2. Replace all Variable type tokens with unique placeholders __hdx_sentinel_N in a sentinelString, and track the locations of the placeholders within the sentinelString. We do this to ensure that the sentinelString can be parsed as valid lucene (${var} reference are not valid single lucene terms).
  3. Parse the sentinelString as lucene
  4. Rewrite the terms representing Field:"Value" terms (from the AST), where Value is a tracked placeholder string in the sentinelString.

The transform is a lucene --> lucene transformation, so the resulting lucene still goes through the existing lucene --> SQL transpiler and inherits all of its optimizations.

Screenshots or video

Some examples:

Screenshot 2026-08-24 at 3 02 01 PM Screenshot 2026-08-24 at 3 03 07 PM Screenshot 2026-08-24 at 3 02 52 PM Screenshot 2026-08-24 at 3 02 34 PM Screenshot 2026-08-24 at 3 02 15 PM

How to test on Vercel preview

  • Create a dashboard and add some variables via the Edit Filters and Variables button
  • Create a chart and reference lucene variables. Inspect the SQL generated for various conditions

References

  • Linear Issue: Closes HDX-5156
  • Related PRs:

@changeset-bot

changeset-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 73d8a12

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@hyperdx/app Patch
@hyperdx/common-utils Patch
@hyperdx/api Patch
@hyperdx/otel-collector Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Aug 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview Aug 25, 2026 8:03pm
hyperdx-storybook Ready Ready Preview Aug 25, 2026 8:03pm

Request Review

@greptile-apps

greptile-apps Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds exact-match expansion for quoted Lucene variable references and fixes the previously reported fallback for supported URL and escaped-colon syntax.

  • Tokenizes variable-bearing Lucene expressions and parses them through temporary sentinel terms.
  • Distributes quoted field references into exact-match OR clauses while retaining grouped substring behavior for unquoted references.
  • Adds parser, substitution, completion-preview, escaping, negation, and empty-selection coverage.
  • Refactors variable substitution to carry an explicit input language.

Confidence Score: 4/5

The PR should not merge until literal internal-placeholder text is preserved during Lucene variable rewriting.

The new source decoder silently changes valid user-authored Lucene terms that happen to match its internal placeholder names whenever the query also enters the variable rewrite path.

Files Needing Attention: packages/common-utils/src/queryParser.ts and packages/common-utils/src/variables.ts

Important Files Changed

Filename Overview
packages/common-utils/src/variables.ts Implements context-aware substitution and AST-guided exact-match distribution; its source restoration exposes collisions with literal internal placeholders.
packages/common-utils/src/queryParser.ts Centralizes reversible parser-token encoding, but the decoder cannot identify whether a matching placeholder originated from encoding or user input.
packages/common-utils/src/tests/variables.test.ts Adds broad coverage for exact-match distribution, special syntax, escaping, negation, multiple references, and empty selections.
packages/app/src/components/SQLEditor/variableCompletions.tsx Updates completion previews and descriptions to reflect language-aware Lucene exact-match behavior.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Lucene template] --> B[Tokenize variables and macros]
  B --> C[Encode parser-sensitive source text]
  C --> D[Insert sentinel terms]
  D --> E[Parse Lucene AST]
  E --> F{Quoted field variable?}
  F -->|Yes| G[Distribute field over exact-match values]
  F -->|No| H[Apply ordinary variable expansion]
  G --> I[Decode untouched source text]
  H --> I
  I --> J[Lucene-to-SQL parser]
Loading

Fix all with Greploop Fix All in Claude Code Fix All in Conductor Fix All in Cursor Fix All in Codex

Reviews (3): Last reviewed commit: "review: Address review feedback" | Re-trigger Greptile

Comment thread packages/common-utils/src/variables.ts
@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 305 passed • 1 skipped • 1007s

Status Count
✅ Passed 305
❌ Failed 0
⚠️ Flaky 3
⏭️ Skipped 1

Tests ran across 4 shards in parallel.

View full report →

@pulpdrew
pulpdrew marked this pull request as ready for review August 24, 2026 19:39
@github-actions github-actions Bot added the review/tier-3 Standard — full human review required label Aug 24, 2026
@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

🟡 Tier 3 — Standard

Introduces new logic, modifies core functionality, or touches areas with non-trivial risk.

Why this tier:

  • Diff size: 411 production lines changed (Tier 2 max: < 250)
  • Cross-layer change: touches frontend (packages/app) + shared utils (packages/common-utils)

Review process: Full human review — logic, architecture, edge cases.
SLA: First-pass feedback within 1 business day.

Stats
  • Production files changed: 4
  • Production lines changed: 411 (+ 440 in test files, excluded from tier calculation)
  • Branch: drew/lucene-variable-distribute
  • Author: pulpdrew

To override this classification, remove the review/tier-3 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Deep Review

Scope: feat: Distribute exact-match lucene variable references (PR #2987), diffed against 7f3878b. Rewrites quoted lucene variable references Field:"$var" into a distributed exact-match form (Field:"api" OR Field:"web") via a tokenize → sentinel → parse → rewrite pipeline in packages/common-utils/src/variables.ts, backed by a refactored special-token encode/decode table in packages/common-utils/src/queryParser.ts.

No critical issues found. The offset arithmetic is consistent (recorded sentinel offsets and AST term offsets both live in encoded space), variable values never enter the sentinel string so they cannot collide with placeholders, the encode-before-parse fix for URL/escaped-colon syntax is in place with regression tests, and all three prior review comments are resolved. The items below are recommended follow-ups, not merge blockers.

🟡 P2 -- recommended

  • packages/common-utils/src/variables.ts:734 -- variables.ts hand-reproduces queryParser's private encode-before-parse contract (encoding each text token, parsing the raw sentinel string, then decoding untouched slices), so all offset math is silently coupled to the exact placeholder lengths in SPECIAL_TOKEN_ENCODINGS; a future edit to that table can break the rewrite with no compile-time signal.
    • Fix: Expose a single parser-owned helper that returns AST terms with offsets already mapped to source coordinates, and keep encodeSpecialTokens/decodeSpecialTokensToSource/IMPLICIT_FIELD internal to queryParser.
    • maintainability
  • packages/common-utils/src/__tests__/variables.test.ts:615 -- no test feeds a variable value containing an encoding placeholder (HDX_COLON, http_COLON_//, HDX_BACKSLASH_LITERAL) or the sentinel literal __hdx_sentinel_0, which is exactly the collision path the sentinel/decode design is most likely to regress on.
    • Fix: Add distributed-rewrite cases whose selected values contain each placeholder literal and the sentinel string, asserting they appear verbatim in the distributed output.
    • testing
🔵 P3 nitpicks (6)
  • packages/common-utils/src/variables.ts:734 -- a template text literal such as HDX_COLON or http_COLON_// typed by an author is decoded back into \: / http:// on the untouched-slice path, corrupting the literal; likelihood is very low and it shares the pre-existing collision class already present in the SQL compile path.
    • Fix: Document the placeholder-collision limitation, or use collision-resistant sentinels/placeholders unlikely to appear in author input.
  • packages/common-utils/src/__tests__/variables.test.ts:740 -- special sequences (http://, localhost:PORT, a bare colon) are exercised only as surrounding text, never as a selected variable value, leaving the distributed value path unverified for them.
    • Fix: Add a case where the value itself is http://example.com / localhost:3000, asserting it is emitted verbatim inside the quoted terms.
  • packages/common-utils/src/__tests__/variables.test.ts:648 -- no test references the same variable twice in quoted form in one template, so repeated-value distribution and sequential runStart advancement go uncovered.
    • Fix: Add expand('ServiceName:"$service" OR Host:"$service"', [SERVICE]) asserting both references distribute independently.
  • packages/common-utils/src/variables.ts:719 -- the plain "expand every token" fallback is duplicated three times (early return, parse-failure catch, per-token loop branch), inviting drift.
    • Fix: Extract a single expandAllPlainly(tokens, ctx) helper and call it from all three sites.
  • packages/common-utils/src/queryParser.ts:37 -- each SPECIAL_TOKEN_ENCODINGS row carries both value (lucene-escaping dropped) and source (original spelling), differing only for the backslash and escaped-colon rows; picking the wrong decoder is a silent correctness bug.
    • Fix: Add a comment at the table site contrasting value vs source so the distinction is visible where rows are edited.
  • packages/common-utils/src/variables.ts:796 -- the renamed export substituteWithContext names the parameter shape rather than the operation; the test file re-introduces a local substituteVariables wrapper to keep call sites readable.
    • Fix: Prefer an intent-named export such as substituteVariables(input, ctx) that still takes the context object.

Reviewers (3): testing, maintainability, previous-comments. Correctness and adversarial dimensions were additionally traced at the orchestrator level (offset-space consistency, placeholder/sentinel collision, negation and empty-selection handling); no correctness defect was confirmed.

Testing gaps:

  • Large selection lists distribute to one field:"value" clause per value with no cap — uncapped expansion is unverified.
  • -Field:"$var" with an empty selection compiles to NOT ((1=1)), which matches nothing and empties the tile until a value is selected; this is pre-existing behavior (applies to unquoted -Field:$var too) and is pinned in queryParser.test.ts, but confirm it matches product intent.

Comment on lines +93 to 99
export function decodeSpecialTokensToSource(query: string): string {
return SPECIAL_TOKEN_ENCODINGS.reduce(
(decoded, { decodePattern, source }) =>
decoded.replace(decodePattern, source),
query,
);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes here were introduced so that a lossless encode/decoding could be used to (a) encode before parsing lucene in variables.ts and (b) decode back to the exact original input instead of what decodeSpecialTokens returns, which is slightly lossy in some cases because it returns unescaped values.

@pulpdrew
pulpdrew requested a review from wrn14897 August 24, 2026 19:51
disableMacros: isLucene,
return substituteWithContext(input, {
variables,
defaultFormat: inputLanguage === 'lucene' ? 'lucene' : 'sqlstring',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this sqlstring or sql?

const SearchConditionRequiredLanguageSchema = z.enum([
'sql',
'lucene',
'promql',
]);

I feel like we should probably add an enum for the search condition language.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's sqlstring, which is a specific VariableFormat (existing type), not the language.

I have updated the types to NonNullable<SearchConditionLanguage> though for additional strictness, I think it makes sense to re-use the existing enum but let me know if you disagree!

Comment thread packages/app/src/components/SQLEditor/variableCompletions.tsx Outdated
wrn14897
wrn14897 previously approved these changes Aug 25, 2026

@wrn14897 wrn14897 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few non-blocking nits. Overall, it looks good. Nice improvement!

Comment thread packages/common-utils/src/queryParser.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge review/tier-3 Standard — full human review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants