-
-
Notifications
You must be signed in to change notification settings - Fork 128
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
Bodybuilder v3.0 #282
Comments
Removing single chaining would be a mistake. Changing an API used by 100s if not 1000s of devs is a bad idea. That said I like the direction, as it relates to the simplification and lazy loading. |
I like this! Moving to a more simplified api would allow for some good typing upgrades, and an overall boost to developer experience. I agree with @ferronrsmith in that we probably want to keep the single chaining. Regarding point 3, I think exposing the builder classes could be cool. For example, in a past version of our ES setup we would pass around a single instance of BB to many methods before sending it to ES: class ProductAggBuilder {
buildAgg(bb: BodyBuilder) {
return bb.agg(...)
}
) This got a bit cumbersome with different business logic, and ended up being tough to follow. With the exposed classes, we could have written the above method like: class ProductAggBuilder {
buildAgg() {
return new AggregationBuilder(...)
}
) I'm not sure if that's how others use BB, but having the exposed builders as an option would let devs write code that is easier to read and test, IMO. Our usage has since changed, but we still rely on BB for all our ES stuff at the office. I know we have done a lot of wrapping/hacking to get around some things, so I can check with some of the devs for any feedback/input they have and get back to you! |
Thank you both very much for your candid feedback. I moved back to single chaining. It turns out that TS by now has all the tools to allow you to merge multiple objects/builders into one. The above example now looks like this const bb = bodyBuilder();
bb.must('match', 'message', 'this is a test')
.filter('term', 'user', 'kimchy')
.filter('term', 'user', 'herald')
.should('term', 'user', 'johnny')
.mustNot('term', 'user', 'cassie')
.aggregation('agg_terms_user', 'terms', 'user')
.aggregation(
'agg_diversified_sampler_user.id',
'diversified_sampler',
{
field: 'user.id',
shard_size: 200,
},
bodyBuilder({
aggs: {
keywords: {
significant_terms: {
field: 'text',
},
},
},
})
);
const result = bb.build(); Feel free to explore the code base at https://github.com/johannes-scharlach/elasticbuilder/tree/composing-builders Some important goals that have been reached are
@quinnlangille thank you very much for the detail, I used to have similar problems and workarounds. I'm looking forward to more real life use cases, since I don't use bodybuilder at work anymore :/ Open questions:
|
I think having a tighter syntax for nested aggs would make the api even more flexible than the last-argument-must-be-nested-callback status quo. For example: const agg = bb.aggregation(
'agg_diversified_sampler_user',
'diversified_sampler',
{
field: 'user',
shard_size: 200,
nested: bb.aggregation(
'agg_user_id',
'term'
{
field: 'id'
}
)
},
); To me, this would be a big win for new/onboarding devs + general readability.
Even though the query api is less complex than the agg api, I think it would benefit from the same treatment by using a strongly typed config object in lieu of many arguments. ex: bb.must({ type: 'match', field: 'message', query: 'this is a test' })
.filter({ type: 'term', field: 'user', query: 'kimchy' }) Something like this would let a developer programatically construct their BB chain arguments with solid type safety. It would also making changing the api more straightforward/safer for devs working on the BB package itself. For example, if ES changes in a dramatic way and BB wants to support, the status quo would be to add more overloads and further complicate the API. Using a config object like in the example above would let us just add optional fields. There are some downsides to that approach as well, the biggest (IMO) is that it would be more difficult to visually parse than status quo if you do know BBs api already. Also, I should note that I'm totally biased here - an implementation like this would better suit my teams usage. I'm not ramped up on other/common usages of BB, so could be way off base. |
I don't really see the difference between your example for nested aggregations and const agg: AggregationBuilder = AB().aggregation(
'agg_diversified_sampler_user',
'diversified_sampler',
{
field: 'user',
shard_size: 200,
},
BB().aggregation(
'agg_user_id',
'term',
{ field: 'id' }
)
); but my question is if additionally we should also allow const agg: AggregationBuilder = AB().aggregation(
'agg_diversified_sampler_user',
'diversified_sampler',
{
field: 'user',
shard_size: 200,
},
(ab: AggregationBuilder) => {
ab.aggregation('agg_user_id', 'term', { field: 'id' });
}
); I definitely don't want an object based approach that is different to existing ES syntax. Using a different syntax must lead to simplification in building queries (and I think it's questionable if I can warm up to qb.must({ match: { message: 'this is a test' } })
.filter({ term: { user: 'kimchy' } }) which could also be strongly typed (though any strong typing requires us to type the full ES query and aggregation API, which was so far a non-goal of this library) |
Yes that's a good point, and I'll admit the "spirit" of the lib is a perspective that I'm totally lacking. If providing exhaustive types for ES is not something the lib wants to do, then I don't think the object based approach is a direction it should take either. I wonder if there is some level of typing we could use without having to integrate the full ES query and it's APIs? We could probably add enums for the builder re: the nested agg example, I was just spitballing so no worries lol. I don't see a need to keep the callback-style syntax (other than less migration), passing |
@danpaz I'd really appreciate your feedback here, too |
@johannes-scharlach thank you for starting this proposal, the discussion here has surfaced several problems I wasn't really aware of with usage of bodybuilder, since I also don't use bb or elasticsearch at work anymore. Let me see if I can summarize the main problems to solve that have been raised in the discussion so far:
My question to the group here is: do we actually need a major version bump to solve these problems? Certainly 3 would require a major bump to slim the API surface, but I feel like 1 and 2 could be added in a non-breaking way. E.g. we could continue to support the nested callbacks, and also expose the nested builders. Similarly, perhaps we could add a config-object call style without changing the list-of-strings style for the most basic usage. My feeling is that bodybuilder is one of those libraries that ought to "just work" without too much thought from the developer. I think that's one of the reasons it's still popular after 5(!) years. We should just be sure that a major bump provides enough benefit to the user to justify the change. I want to emphasize that though I was the original author of this library, these are really only my opinions and, especially since I don't use the library anymore, should be taken as input rather than decision :) |
Given our recent discussions in #281 I had a bit of an itch in my fingers to try if a new and simpler API to bodybuilder could be created given everything we've learnt over the years and https://github.com/johannes-scharlach/elasticbuilder is my approach to that. It already passes most of the test cases which have been written for this repository, which seems like a big step to prove that it indeed is possible to provide the same functionality with what is hopefully a smaller interface.
@danpaz @ferronrsmith (and if you're interested also @quinnlangille) I'm looking forward to your feedback to see if we can/should turn that into v3 of bodybuilder. It might not be trivial to find a migration path, since it would be a complete rewrite of the library.
Example usage:
which brings up a couple of new questions:
new AggregationBuilder
and these nested queries being evaluated lazily when you callbuild()
. This would have helped me in the past, but is it worth it to expose those new concepts?I'm happy to continue the work in this repo if you're interested in v3 or I might even go in the direction of generalising the builder interface description as @ferronrsmith suggested to also construct other queries (e.g. mongodb).
The text was updated successfully, but these errors were encountered: