Skip to content

BsonPath

sealed interface BsonPath

Access specific fields in arbitrary BSON documents using a JSONPath-like API.

To access fields of a BSON document, use select or at.

Why BSON paths?

Most of the time, users want to deserialize documents, which they can do with opensavvy.ktmongo.bson.read.

However, sometimes, we receive large BSON payloads but only care about a few fields (for example, an explain plan). Writing an entire DTO for such payloads is time-consuming and complex.

Deserializing only a few specific fields can be much faster than deserializing the entire payload, as BSON is designed to allow skipping unwanted fields.

We may also face a payload that is too dynamic to easily deserialize, or with so much nesting that accessing fields becomes boilerplate.

In these situations, it may be easier (and often, more performant) to only deserialize a few specific fields, which is what BsonPath is useful for.

Syntax

BsonPath["foo"]    // Refer to the field 'foo': $.foo
BsonPath[0]        // Refer to the item at index 0: $[0]
BsonPath["foo"][0] // Refer to the item at index 0 in the array named 'foo': $.foo[0]

Accessing data

Find the first value for a given BSON path using at:

val document: Bson = 

val name = document at BsonPath["profile"]["name"]

Find all values for a given BSON path using select:

val document: Bson = 

document.select(BsonPath["profile"])
    .forEach { println("Found $it") }

Inheritors

Constructors

BsonPath

fun BsonPath(@Language



(value = "JSONPath") text: String): BsonPath

Parses an RFC-9535 compliant string expression into a BsonPath instance.

This function is the mirror of the BsonPath.toString methods.

Warning. Not everything from the RFC is implemented at the moment. As a rule of thumb, if text can be returned by the BsonPath.toString function of a segment, then it can be parsed by this function.

  • $: The root identifier. See BsonPath.Root.
  • ['foo'] or .foo: Accessor for a field named foo. See BsonPath.get.
  • [0]: Accessor for the first item of an array. See BsonPath.get.
  • .* or [*]: Accessor for all direct children. See BsonPath.all.
  • [1:3]: Accessor for elements at index 1..<3. See BsonPath.sliced.
  • [?@.a > @.b]: Accessor for elements that satisfy the given condition.

Multiple selectors can be defined in the same brackets. When this is the case, all nodes that match any of the selectors are returned. For example, ['foo', 'bar', 'baz'] will return the values of the fields foo, bar and baz. See BsonPath.any.

Examples
val document: Bson = 

val id: Uuid = document at BsonPath("$.profile.id")

for (user in document.select<User>(BsonPath("$.friends"))) {
    println("User: $user")
}

See also

  • BsonPath: Learn more about BSON paths.

  • at: Access one field by its BSON path.

  • select: Access multiple fields by their BSON path.

Types

Current

data object Current : BsonPath

The current node, noted @.

PathOrSelector

Marker interface for types that implement both BsonPath and Selector.

Root

object Root : BsonPath

The root of a BsonPath expression.

Selector

sealed interface Selector

Represents a unique selector in a multi-selector segment.

Properties

all

open val all: BsonPath

Points to all children of a document.

  • The children of a BsonArray are its elements.

  • The children of a Bson document are the values of its fields.

Example
BsonPath["foo"].all   // $.foo.*

parent

abstract val parent: BsonPath?

The parent path of this path: the same path without the last segment.

For example, the path $.foo.bar has the parent $.foo.

The root path has the parent null. All other paths have a non-null parent.

Functions

any

open fun any(vararg selectors: BsonPath.Selector): BsonPath

Allows specifying multiple selectors.

All elements that match a selector are returned, in the order of the selectors. For example, the path $[0, 3] returns the elements at index 0 and at index 3.

The same element may be returned multiple times if it matches multiple selectors.

The Selector type is obtained by the top-level functions on BsonPath.Root. Non-top-level paths are not allowed by the JSONPath specification.

Examples
["a", "b", "c", "d", "e", "f", "g"]
  • BsonPath.any(BsonPath[0], BsonPath[3]) returns ["a", "d"]

  • BsonPath.any(BsonPath.sliced(0, 2), BsonPath[5]) returns ["a", "b", "f"]

  • BsonPath.any(BsonPath[0], BsonPath[0]) returns ["a", "a"]

findIn

@LowLevelApi



abstract fun findIn(reader: BsonValueReader): Sequence<BsonValueReader>

Applies the filters described by this path on the reader.

Implementation notes

The reader is the root data. The path should recursively search by applying the parent's findIn first.

get

open operator fun get(field: String): BsonPath

Points to a field in a Bson document.

Example
BsonPath["foo"]         // $.foo
BsonPath["foo"]["bar"]  // $.foo.bar
open operator fun get(index: Int): BsonPath

Points to the element at index in a BsonArray.

Example
BsonPath[0]         // $[0]
BsonPath["foo"][1]  // $.foo[1], the first element
BsonPath["foo"][-1] // $.foo[-1], the very least element

reversed

open fun reversed(): BsonPath

Iterates a BsonArray in the reversed order, starting from the end.

If the node is not an array, nothing is returned.

This is a shorthand syntax for a slice of [::-1].

sliced

open fun sliced(range: IntProgression): BsonPath

Points to the elements of a BsonArray at the indices selected by range.

If the node is not an array, nothing is returned.

To create an open-ended range, use the overload that accepts integers.

To reverse an array, see reversed.

Range normalization

When a range has a step, the IntProgression class can reduce the closing bound if the step does not reach it. For example, 1 .. 6 step 2 becomes 1 .. 5 step 2 because both ranges cover the values [1, 3, 5] and 6 could not have been included. This doesn't impact the outputs, but may impact the toString representation of this path.

Examples
BsonPath.sliced(1..<5)         // $[1:5]: Items at indices 1 (inclusive) to 5 (exclusive)
BsonPath.sliced(1..5]          // $[1:6]: Items at indices 1 (inclusive) to 5 (inclusive)
BsonPath.sliced(1..5 step 2)   // $[1:6:2]: Items at indices 1 (inclusive) to 5 (inclusive), skipping every other item
BsonPath.sliced(5 downTo 1)    // $[1:6:2]: Items at indices 1 (inclusive) to 5 (inclusive), in reverse order

See also

open fun sliced(
    start: Int? = null, 
    end: Int? = null, 
    step: Int = 1
): BsonPath

Points to the elements of a BsonArray at the indices selected by start and end, with an optional step.

If the node is not an array, nothing is returned.

Elements can be iterated in the reversed order by having a start greater than the end and having a negative step.

If the smaller bound is null, it means "the first element of the array, inclusive". If the larger bound is null, it means "the last element of the array, inclusive".

Examples
[0, 1, 2, 3, 4, 5, 6]
  • BsonPath.sliced(1, 3) returns [1, 2]

  • BsonPath.sliced(start = 5) returns [5, 6]

  • BsonPath.sliced(1, 5, 2) returns [1, 3]

  • BsonPath.sliced(5, 1, -2) returns [5, 3]

  • BsonPath.sliced(step = -1) returns [6, 5, 4, 3, 2, 1, 0]

Parameters

  • start: The index at which the slice should start, inclusive.

  • end: The index at which the slice should end, exclusive.

  • step: How many elements should be skipped between found elements.

  • With step of 1, all elements are returned.

  • With step of 2, every other element is returned.

  • With step of 0, no elements are returned at all.

See also