-
Notifications
You must be signed in to change notification settings - Fork 8.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Expressions] Use table column ID instead of name when set #99724
Conversation
Pinging @elastic/kibana-app-services (Team:AppServices) |
const existingColumnIndex = columns.findIndex(({ name }) => name === args.name); | ||
const existingColumnIndex = columns.findIndex(({ id, name }) => { | ||
// Columns that have IDs are allowed to have duplicate names, for example esaggs | ||
if (id) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we also check whether args.id
is set as well here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@flash1293 @poffdeluxe I've just changed the logic here since your last review. I'm now checking id === args.name
if the column has an ID, which makes sense to me as a fallback. What do you all think of this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested this across several workpads using mapcolumn and math and everything worked great still
const existingColumnIndex = columns.findIndex(({ id, name }) => { | ||
// Columns that have IDs are allowed to have duplicate names, for example esaggs | ||
if (args.id) { | ||
return id === args.id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the args.id
is passed but the column does not have id
is it ok to return false
?
I didn't see any test about this. I assume it's ok, but probably it's better to make it clear somewhere (either with a test or documentation).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dej611 good catch, I thought I had a test for this but I will add one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left only a comment about some edge case clarification.
Apart from that, LGTM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Paraphrasing the new logic to make sure I understood correctly:
- If id is set, id is used to find a column
- If id is not available, name is used as fallback
- It's not possible to reference by name if an id is present
@elasticmachine merge upstream |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AppServices changes LGTM.
Sorry to all the code reviewers, but I've changed the behavior of this PR to make it much simpler. It turns out that I was making a bad assumption about the lack of I have written a much more detailed set of examples in the PR description up at the top. I hope you will all take some time to re-review as I think this is in a much better state now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have two nits, but otherwise this looks good to me after having played around with it a bit.
As names aren't unique anymore, it's a bit weird to only reference a column by name now - first one wins which makes kind of sense, but maybe we can introduce a breaking change in 8.0 to require the id to simplify this logic. I think it's fine for now though.
@@ -114,7 +122,7 @@ export const mapColumn: ExpressionFunctionDefinition< | |||
} | |||
|
|||
if (existingColumnIndex === -1) { | |||
columns.push(newColumn); | |||
columns[columnLength] = newColumn; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is wrong with using .push
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously the existingColumnIndex
was updated for each row, now it's updated once. So if I used .push
it would add a new column for each row.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this executed once per row? It seems like only https://github.com/elastic/kibana/pull/99724/files/9f4d6474708ba8f11375fc81893cc153bb87ba7c#diff-30a63ac89404e0e4c5cebad82ffb721533d9aae11810cb2d982d1b45d6fecbbfR102-R109 is executed per row, the rest of the code is running only once per mapColumn
invocation. Promise.all
returns a single promise which is resolved once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah, I think I just misread this part. Will update.
const mathContext = isDatatable(input) | ||
? pivotObjectArray( | ||
input.rows, | ||
input.columns.map((col) => col.name) | ||
input.columns.map((col) => col.id ?? col.name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary? If we can be sure there is an id now, we can simply use input.columns.map((col) => col.id)
, right?
@@ -44,7 +44,7 @@ export const mapColumn: ExpressionFunctionDefinition< | |||
types: ['string', 'null'], | |||
help: i18n.translate('expressions.functions.mapColumn.args.idHelpText', { | |||
defaultMessage: | |||
'An optional id of the resulting column. When `null` the name/column argument is used as id.', | |||
'An optional id of the resulting column. When no id is provided, the id will be looked up from the existing column and default to the name.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had trouble inferring everything that happens from this description. Maybe like this? ... will be looked up from the existing column by the provided name argument. If no column with this name exists yet, a new column with this name and an identical id will be added to the table
Thanks for the changes, looks great to me! |
@elasticmachine merge upstream |
return name === args.name; | ||
}); | ||
const columnId = | ||
existingColumnIndex === -1 ? args.id ?? args.name : columns[existingColumnIndex].id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ids should be unique, if we find existing column with requested new column id we should throw
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imo the logic should be:
const columnId = args.id ?? args.name;
if(columns.find({id} => id === columnId)) throw('id already exists');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ppisljar Column IDs are guaranteed to be unique with this logic, I am not seeing any case where you could get a duplicate ID. Also, this logic lets you rewrite a column by ID, this is shown in some of the test cases. For example mapColumn id="existing" name="New name" expression={math "existing + 2"}
is a valid expression that updates the data in place.
const mathContext = isDatatable(input) | ||
? pivotObjectArray( | ||
input.rows, | ||
input.columns.map((col) => col.name) | ||
input.columns.map((col) => col.id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is meant by fallback to names ? the fact that name will be used for columnId when id was not provided (to map_column for example ?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, if no id
is provided we find the first column with the name. This is required for backwards compatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@elasticmachine merge upstream |
💚 Build SucceededMetrics [docs]Page load bundle
Unknown metric groupsReferences to deprecated APIs
History
To update your PR or re-run it, just comment with: |
…9724) * [Expressions] Use table column ID instead of name when set * Update ID matching to match by name sometimes * Add an extra case to prevent insertion of duplicate column * Simplify logic and add test for output ID * Respond to review comments Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
…101110) * [Expressions] Use table column ID instead of name when set * Update ID matching to match by name sometimes * Add an extra case to prevent insertion of duplicate column * Simplify logic and add test for output ID * Respond to review comments Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
…sens/kibana into reporting/new-png-pdf-report-type * 'reporting/new-png-pdf-report-type' of github.com:jloleysens/kibana: (46 commits) [Security Solution] Add Ransomware canary advanced policy option (elastic#101068) [Exploratory view] Core web vitals (elastic#100320) [Security solution][Endpoint] Add unit tests for fleet event filters/trusted apps cards (elastic#101034) [Lens] Use a setter function for the dimension panel (elastic#101123) [Index Patterns] Fix return saved index pattern object (elastic#101051) [CI] For PRs, build TS refs before public api docs check (elastic#100791) [Maps] fix line and polygon label regression (elastic#101085) Migrate CCR to new ES JS client. (elastic#100131) [Canvas] Switch Canvas to use React Router (elastic#100579) [Expressions] Use table column ID instead of name when set (elastic#99724) [DOCS] Updates docs landing page (elastic#100749) [DOCS] Corrects typo in step 3 (elastic#101079) [DOCS] Updates runtime example in Discover (elastic#100926) Migrate kibana.autocomplete config to data plugin (elastic#100586) [Uptime] New width/delay definition for waterfall sidebar item tooltip (elastic#100147) [FTR] Use importExport for saved_object/basic archive (elastic#100244) [Fleet] Better input for multi text input in agent policy builder (elastic#101020) [CI] Buildkite support with Baseline pipeline (elastic#100492) [Reporting/Telemetry] Do not send telemetry if we are in screenshot mode (elastic#100388) Create API keys with metadata (elastic#100682) ...
Lens uses the
esaggs
expression function which always has a different columnid
andname
. Theid
of the column represents the JSON key where the data is stored in each row, for example{ 'col-0-a': 0 }
has an ID ofcol-0-a
and a name ofbytes
.This PR fixes the
mapColumn
behavior when the optionalid
parameter is set. In a previous iteration of this PR, the fix was to introduce a complex set of lookups. This is no longer the approach, instead the logic is simple, and can easily be copied as a fix for another set of bugs in Canvas. The new behavior is:mapColumn
is called without an ID, look up the first matching column by name, and use that ID. Example:id
andname
, look up only by id. In this example, the name is changed by the mapColumn call, but the ID is kept:Previous version of this PR
For code reviewers who previously reviewed this change, the behavior described above is not what was originally implemented. The main reason for this is that all datatable columns have an ID property, which I had missed due to the way I was testing. Also, @cqliu1 fixed the last remaining datatable that was missing an ID property in #98561. You can verify this by opening canvas and typing
essql "select bytes from kibana_sample_data_logs
| render as="debug", which produces a table with a matching ID and name. Because we can now guarantee that
id` is set on the table, we don't need the complex fallback logic.Checklist