Skip to content
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

Add nested field support to KQL #47070

Merged
merged 78 commits into from
Oct 30, 2019
Merged

Add nested field support to KQL #47070

merged 78 commits into from
Oct 30, 2019

Conversation

Bargs
Copy link
Contributor

@Bargs Bargs commented Oct 1, 2019

Summary

Closes #44554

This PR adds a new syntax to KQL for querying nested fields.

Nested fields can be queried in two different ways:

  1. Parts of the query may only match a single nested doc (bool inside nested). This is what most people want when querying on a nested field.
  2. Parts of the query may match different nested docs (nested inside bool). This is how a regular object field works but nested fields can be queried in the same way. Although generally less useful, there are occasions where one might want to query a nested field in this way.

The new KQL syntax supports both. Say we have the following document, where level1 and level2 are both nested fields:

{
  "level1": [
    {
      "level2": [
        {
          "foo": "qwe"
          "bar": "asd"
        },
        {
          "foo": "zxc"
          "bar": "rty"
        }
      ]
    },
    {
      "level2": [
        {
          "foo": "fgh"
          "bar": "vbn"
        },
        {
          "foo": "uio"
          "bar": "jkl"
        }
      ]
    }
  ]
}

If we wanted to match a single level2 document we could write a query like this:

level1.level2:{ foo:qwe and bar:asd }

The first part (level1.level2) is the nested path. Everything inside the curly braces (the "nested group") must match a single document. So for example, level1.level2:{foo:qwe and bar:rty} would not match because there isn't a single nested document that matches the entire query in the nested group. This is the first way to query a nested field listed above (bool inside nested).

What if we wanted to match a parent doc that contains a document where foo = qwe and a document where bar = rty, but it's ok if those are two different sub documents? This is the second way of querying a nested field (nested inside bool). We can do this:

level1.level2:{ foo:qwe } and level1.level2:{ bar:rty }

Since the individual query clauses are in separate nested groups they are allowed to match separate documents.

Common questions

  • In the above scenario, why don't we just auto-detect that a nested field is being queried and save the user some typing?

We could, but it sets the user up for failure. Most users will want to use nested fields to find individual sub-documents that match a query. If we allow querying nested fields without the special syntax, many users will end up with something like this:

level1.level2.foo:qwe and level1.level2.bar:asd

Each query clause here could match a different sub-document, which is likely not what a user expects. By requiring the special syntax for any nested query we help the user understand that there is an extra decision to be made here. Furthermore, we selecting a nested field via the autocomplete we will automatically add the necessary path and curly braces, so there isn't really any extra typing to be done.

  • Why do we require the path if we already know what it is for each field in the index pattern by reading field_caps?

The user will need this flexibility in the scenario where they have nested fields that contain other nested fields. They need the ability to control at what level the nested grouping occurs. Extending the previous example, let's say we want to match an individual document at level1, but we don't care if multiple documents match at level2. We can do this:

level1:{ level2:{ foo:qwe } and level2:{ bar:rty } }

This query would match our document, because the nested grouping is happening at level1. foo:qwe and bar:rty each exist in a single level1 document, even though they're in separate level2 documents. However the next query would fail, because the matching docs are not even in the same level1 document:

level1:{ level2:{ foo:qwe } and level2:{ bar:jkl } }

UI

Aside from the KQL syntax updates there are a few additions to the UI.

When selecting a nested field in the autocomplete for the first time, we display a helpful notification explaining that there is a special syntax for nested fields and point them to the docs explaining how it works:

image

When selecting a nested field in the autocomplete, we auto-add the required nested field syntax. The autocomplete also only suggests valid sub-fields inside of a nested group:

Oct-01-2019 14-49-28

If you attempt to query a nested field without a nested group you'll get an error:

image

We also have helpful errors when there is a nested group but the path is not correct, and when a non-nested field is used inside a nested group.

TODO

  • Update code that was already using parent/subType properties on index pattern
  • Write migration for changes to parent/subType properties on index pattern
  • Write docs
  • Update and add new tests
    • autocomplete
    • getFullFieldNameNode

Checklist

Use strikethroughs to remove checklist items you don't feel are applicable to this PR.

For maintainers

where the nested parent is not the immediate parent.
in KQL expression. Also auto-wrap nested fields that are included in
a wildcard field name since the KQL syntax doesn't give users a way to
handle this.
could suggest multi fields or plain object fields if you mistakenly
created a nested group with one of their prefixes
@Bargs Bargs added Team:Visualizations Visualization editors, elastic-charts and infrastructure Feature:KQL KQL v8.0.0 v7.5.0 labels Oct 1, 2019
@gchaps
Copy link
Contributor

gchaps commented Oct 28, 2019

@Bargs Thanks for the explanation. Will either of these work?

It looks like you're querying on a nested field. KQL has a special syntax for working with nested fields, and we filled in a query for you. For more suggestions, check out our docs.

It looks like you're querying on a nested field. You can construct KQL syntax for nested queries in different ways, depending on the results you want. Learn more in our docs.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

docs/discover/kuery.asciidoc Outdated Show resolved Hide resolved
docs/discover/kuery.asciidoc Outdated Show resolved Hide resolved
docs/discover/kuery.asciidoc Outdated Show resolved Hide resolved
docs/discover/kuery.asciidoc Outdated Show resolved Hide resolved
docs/discover/kuery.asciidoc Outdated Show resolved Hide resolved
docs/discover/kuery.asciidoc Outdated Show resolved Hide resolved
docs/discover/kuery.asciidoc Outdated Show resolved Hide resolved
docs/discover/kuery.asciidoc Outdated Show resolved Hide resolved
docs/discover/kuery.asciidoc Outdated Show resolved Hide resolved
docs/discover/kuery.asciidoc Outdated Show resolved Hide resolved
Bargs and others added 3 commits October 28, 2019 16:52
@Bargs
Copy link
Contributor Author

Bargs commented Oct 28, 2019

Thanks for all the helpful suggestions @gchaps! I updated the text in the help toast with your second suggestion and I pulled in all your inline suggestions except one which I had a tiny question about.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Bargs and others added 2 commits October 29, 2019 00:36
Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>
@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@Bargs Bargs mentioned this pull request Oct 29, 2019
10 tasks
@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@timroes timroes added v7.6.0 and removed v7.5.0 labels Oct 30, 2019
For example, `items:{ name:banana and stock:9 }` does not match because there isn't a single nested document that
matches the entire query in the nested group.

What if you want to find a store with more than 10 bananas that *also* stocks vegetables? This is the second way of querying a nested field, and you can do it like this:
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe before starting with the more complex example here, let's explain the difference to what items:{ name:banana } and items:{ stock > 10 } actually is. I know that the next paragraph explains that behavior, but I think it might be a bit more clear if we use exactly the same example and show how this works in the "opposite" way, before then continue on an example with a more complex query? cc @gchaps

Copy link
Contributor

Choose a reason for hiding this comment

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

@Bargs I agree with what Tim is saying. Happy to review the updated text when it is ready.

Copy link
Contributor

@timroes timroes left a comment

Choose a reason for hiding this comment

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

Just some minor code/doc comments. Tested on Chrome Linux everything I tested seems to work as expected. LGTM

@Bargs
Copy link
Contributor Author

Bargs commented Oct 30, 2019

@timroes since the only comment left is a docs suggestion I am gonna merge this while it's green and if @gchaps has a suggestion I can update the docs later. Thank you again for the quick review! 🙇 🙇 🙇

@Bargs Bargs merged commit 1dcec9d into elastic:master Oct 30, 2019
Bargs added a commit to Bargs/kibana that referenced this pull request Oct 30, 2019
This PR adds a new syntax to KQL for querying nested fields.

Nested fields can be queried in two different ways:

Parts of the query may only match a single nested doc (bool inside nested). This is what most people want when querying on a nested field.
Parts of the query may match different nested docs (nested inside bool). This is how a regular object field works but nested fields can be queried in the same way. Although generally less useful, there are occasions where one might want to query a nested field in this way.
The new KQL syntax supports both.
Bargs added a commit that referenced this pull request Oct 30, 2019
This PR adds a new syntax to KQL for querying nested fields.

Nested fields can be queried in two different ways:

Parts of the query may only match a single nested doc (bool inside nested). This is what most people want when querying on a nested field.
Parts of the query may match different nested docs (nested inside bool). This is how a regular object field works but nested fields can be queried in the same way. Although generally less useful, there are occasions where one might want to query a nested field in this way.
The new KQL syntax supports both.
@lukasolson lukasolson mentioned this pull request Jun 27, 2022
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:KQL KQL release_note:enhancement Team:Visualizations Visualization editors, elastic-charts and infrastructure v7.6.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support querying nested fields in KQL
6 participants