elastic-aggs
v7.0.0-prerelease-3
Published
Typescript definitions for type-safe Elasticsearch aggregations
Downloads
11
Maintainers
Readme
elastic-aggs
Typescript definitions for type-safe Elasticsearch aggregations
Installation
npm install elastic-aggs
How to use type-safe aggregations
Simply replace import for the Elasticsearch Client interface:
import { Client } from 'elastic-aggs';
The Client
interface provided is itself based on the (peer) Elasticsearch dependancy, but with an additional function prototype for search
that implements strongly typed aggregation specifications and results.
To use the types, you must as a Doc
member to the search parameter that specifies what is stored in your ES index. If you don't have a Doc
member, you're using the standard, Elasticsearch search
definition.
Example
https://user-images.githubusercontent.com/2789075/232029391-fbf8f785-1c1b-4113-9d69-5c040f135c52.mp4
In the example, we first add a Doc
member that specifes the type of the documents stored in the Elasticsearch index. This triggers the additional function prototype, which then complains because there is no aggs
member.
As the aggs
are entered, we're prompted for field
values based on the fields in the Doc
. This includes nested field member.
When handling the result, the aggregations
have named, typed members, including nested aggregation results, quickly catching typos and incorrect aggregations.
What's up Doc
?
The Doc
member is not used as run-time. It is only used to carry the type in the additional search
function prototype. To avoid issues with the Elasticsearch library, it should evalue to undefined
. A constant SourceDoc = undefined as unknown
is exported so you can easily type-cast your document type.
const result = await es.search({
Doc: SourceDoc as SomeArbitraryTypeOfTheDocs
...
});
Additionally, the constant AnyDoc
is exported if you only want the aggregation result resolution and don't require field-level results.
Why is it a member and not a type parameter?
Typescript does not handle default type parameters (or variable type parameter lists) appropriately for this kind of call. It needs to capture not only the Doc
but the exact, const (Readonly) type of the search
parameter in order to be able to resolve the aggregations fully. Specifying it as an unused (undefined) parameter makes this possible.
Status
At present, only a limited number of aggregations are provided (see LeafAggregationKeys
and NodeAggregationKeys
in https://github.com/MatAtBread/elastic-aggs/blob/main/typed-aggregations.ts#L122).
The same techniques can be used to implement many more and PRs to do so are welcome.
It doesn't work!
It does, but it requires that strings in particular are const
or Readonly
. This is becuase by default Typescript expands type defintions of string constants to string
:
e = 'abc' // e is of type string
f = 'abc' as const; // f is of type "abc"
The function prototype will capture this in many cases, but if your aggregation is the result of a function call, or other expression where a constant string type is assigned to a mutable or conditional, the "constant" attribute will be lost. Use Readonly<...>
or DeepReadonly<...>
as the return type of such functions to avoid Typescript's native behaviour.