Repository navigation
[BugFix] Keep metadata fields hidden per input of append, appendpipe, appendcol, union and lookup - #5884
Conversation
… appendcol, union and lookup A fields command in any input set the statement-wide isProjectVisited flag, which turns off the final metadata strip, so the inputs without fields returned _id, _index, _score, _maxscore, _sort and _routing as ordinary columns, e.g. source=A | append [ source=B | fields x ]. Each input of append, appendpipe, appendcol and union is now visited with the flag cleared. If some inputs have an explicit project and others do not, the metadata fields of the others are removed before the inputs are combined. If no input has one, the metadata fields are left to the final project as before, so a later fields can still select them. The flag afterwards is the same as before the change. This also fixes SQL SELECT a, b ... UNION ALL SELECT * ..., which failed on mismatched row types because the SELECT * input kept _id. A lookup without output fields provided every field of the lookup table, including its metadata fields. They are now removed unless they are mapping fields, as the output-fields path already does. Signed-off-by: Lantao Jin <ltjin@amazon.com>
append, appendpipe, appendcol, union and lookup
PR Reviewer Guide 🔍(Review updated until commit 923e800)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 923e800
Previous suggestionsSuggestions up to commit 892cb7e
|
Codecov Report❌ Patch coverage is
❌ Your project check has failed because the head coverage (63.82%) is below the target coverage (99.00%). You can increase the head coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #5884 +/- ##
============================================
- Coverage 63.87% 63.82% -0.06%
- Complexity 9091 9093 +2
============================================
Files 962 962
Lines 41033 41074 +41
Branches 4668 4675 +7
============================================
+ Hits 26211 26214 +3
- Misses 13944 13982 +38
Partials 878 878
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| .toList(); | ||
| context.relBuilder.project(projectList); | ||
| } | ||
| } else { |
There was a problem hiding this comment.
lookup silently drops lookup‑side metadata under include_metadata=true.
This new else branch strips the lookup side's metadata unconditionally — it never checks context.isIncludeMetadata(). The other four commands routed through visitInputsWithOwnMetaFields honor the opt‑in via tryToRemoveMetaFields(context, /*force=*/false), which early‑returns on isIncludeMetadata(). Lookup bypasses that helper, so the two policies disagree inside the same PR.
Dataset. Two indices. worker holds employees — id, name, occupation, country, salary. work_information holds department records keyed by uid matching worker.id — uid, name, department, occupation. Both carry the usual OpenSearch metadata: _id, _index, _score, _maxscore, _sort, _routing.
Query. Enrich each worker with the matching department record; explicitly opt into metadata from both sides.
POST /_plugins/_ppl?include_metadata=true
{ "query": "source=worker | LOOKUP work_information uid AS id" }
Expected. Two groups of metadata columns, one per side.
Actual. Only one group — the source side's. The lookup side's _id, _index, _score, _maxscore, _sort, _routing are silently dropped. No error, no warning.
Consistency check. With include_metadata=true and a fields on one side, append, appendpipe, union, appendcol all retain both legs' metadata; only lookup drops it. Users will reasonably assume uniform behavior across the five commands.
Why it matters. include_metadata=true is the opt‑in for callers who need these columns — dedup by _id after enrichment, audit which _index a joined row came from. Half of them quietly disappearing turns the opt‑in into a half‑open door.
No existing or new test covers lookup + include_metadata=true. Please add one asserting both sides' metadata survive.
There was a problem hiding this comment.
I compared include_metadata=true on main and on this branch with plan tests over two tables with distinct _id/_index values. Neither keeps two groups: both sides name their metadata _id, _index, …, so the output holds one column per name.
| Query (include_metadata=true unless noted) | main | This PR |
|---|---|---|
source=worker | lookup work_information uid AS id |
work_information's _id/_index/_score, null on unmatched rows; the worker's own metadata is lost | The worker's _id/_index/_score on every row |
source=worker | lookup work_information uid AS id APPEND occupation |
The worker's | The worker's (unchanged) |
source=worker | lookup work_information uid AS id | fields id, _id (without include_metadata) |
work_information's _id, null on unmatched rows | The worker's _id |
source=worker | join on worker.id = work_information.uid work_information |
No metadata from either side | No metadata from either side (unchanged) |
source=worker | lookup work_information uid AS id OUTPUT _id AS dept_doc_id, _index AS dept_index |
Same as this PR (the output-list path is unchanged) | The worker's _id/_index/_score plus dept_doc_id and dept_index |
With main's behavior, dedup by _id after enrichment collapses every unmatched worker into one row (_id = null). join removes both sides because tryToRemoveMetaFields force-strips join inputs to keep their output schemas unambiguous.
I'll add tests for lookup with include_metadata=true that pin this, and state the include_metadata case in the Javadoc. If you'd rather have include_metadata=true keep the lookup side's metadata, I can switch it, but it would overwrite the source rows' _id again.
Both sides of a lookup name their metadata fields alike, so the output keeps one set. With include_metadata=true it is the source rows' own; before, a lookup without output fields replaced it with the lookup index's, null where no row matched. The lookup index's metadata stays available by name through the output fields. Add plan and integration tests for both, and for the source rows' _id selected after a lookup, and say so in the Javadoc. Signed-off-by: Lantao Jin <ltjin@amazon.com>
|
Persistent review updated to latest commit 923e800 |
Description
With the Calcite engine, a
fieldsin one input of a command that combines inputs exposed the metadata fields (_id,_index,_score,_maxscore,_sort,_routing) of the other input, e.g.source=A | append [ source=B | fields x ]returnedA's metadata columns. The cause is thatCalcitePlanContext.isProjectVisitedis one flag for the whole statement: anyfieldssets it, and the final project removes metadata fields only while it is unset.This PR adds
CalciteRelNodeVisitor.visitInputsWithOwnMetaFields, used byappend,appendpipe,appendcolandunion. It visits each input with the flag cleared. If some inputs have an explicit project and others do not, it removes the metadata fields of the others with the existing non-forcedtryToRemoveMetaFields, soinclude_metadata=truestill keeps them. If no input has a project, nothing changes: the final project removes the metadata fields as before, soappend [ ... ] | fields _idstill works. The flag afterwards has the same value as before this change.appendcol's two copies of the row_number code are now one helper,projectPlusRowNumber.A
lookupwithout an output list provided every field of the lookup index, its metadata fields included. They are now removed unless they are mapping fields, which matches the output-list path (it projects only the mapping and output fields) and the wayjoinremoves the metadata of its right side.The same change fixes SQL
SELECT id, msg FROM t UNION ALL SELECT * FROM tin the unified query API, which failed withCannot compute compatible row type for arguments to set opbecause theSELECT *input kept_id.Behaviour change: when one input has a
fieldsand another does not, the metadata fields of the input withoutfieldscan no longer be referenced after the command, as is already the case afterjoinandmultisearch.Testing:
CalcitePPLMetadataFieldsTest(17 plan tests, over a table with_id,_indexand_scorecolumns) covers each command in both directions, nestedappend, a metadata field selected on one side,lookupby a metadata field,include_metadata=true, and the row alignment ofappendcol.CalcitePPLAppendPipeTest.testAppendPipeInJoinSubsearchchecks thatappendpipeleaves one node on the RelBuilder stack.UnifiedQueryPlannerSqlV2Test.testUnionAllSelectStarAfterExplicitColumnscovers the SQL case.CalcitePPLAppendCommandIT, and the REST API testissues/metadata_fields_multi_input.yml(6 cases).core/atmain, 11 of the 17 plan tests, the SQL test, all 6 REST API cases and the new IT method fail. With the fix,:core:test,:ppl:test,:opensearch:test,:sql:testand:api:testpass with 0 failures. The append, appendpipe, appendcol, union, multisearch, include-metadata, lookup and graphlookup IT classes also pass, with and without pushdown.Not changed: once the flag is set, the final project also skips
tryToRemoveNestedFields, so in the same mixed case an input withoutfieldsmay still return flattened duplicates such asobj.anext toobj.Related Issues
Resolves #5883
Check List
--signoffor-s.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.