- 
                Notifications
    
You must be signed in to change notification settings  - Fork 25
 
Description
Currently, the only way for users to construct instances of relations is via the Builder interface.
This works well enough if users only want to set "standard" fields for a relation (i.e. condition for FilterRels, condition, postJoinFilter, joinType for JoinRels, etc), but we run into limitations quickly if we want to allow users to set other properties like Hints, Remaps, etc. This limitation was originally handled by the introduction of constructor variants like FilterRemap and ProjectRemap which set the standard fields + the extra fields. However the library moved away from this because it would eventually result in a proliferation of methods like FilterHint, FilterRemapHint, etc.
The current approach that is used to solve this was to add methods like Remap(mapping int32) and SetAdvancedExtension(extension *extensions.AdvancedExtension) to the Rel interface to allow users to build a relation, and then set various properties. Ignoring the naming inconsistencies, what's potentially confusing about this approach is that certain fields are treated as immutable (i.e. inputs and fields and relations) because the user has no means to modify them, but other fields (i.e. mapping) can be mutated. It also muddies the Rel interface somewhat, because it contains methods for modelling relations and method for building relation, which are separate concerns.
Instead of having a single central builder for all relations, a different approach would be to introduce modular builders for each relation individually. Something like
type FilterRelBuilder struct {
	input Rel
	condition expr.Expression
}
func NewFilterRelBuilder() *FilterRelBuilder {
	return &FilterRelBuilder{}
}
func (frb *FilterRelBuilder) SetInput(input Rel) *FilterRelBuilder   {}
func (frb *FilterRelBuilder) SetCondition(condition expr.Expression) *FilterRelBuilder {}
func (frb *FilterRelBuilder) SetHint(hint Hint) *FilterRelBuilder {}
...
func (frb *FilterRelBuilder) Build() (FilterRel, []error) {
	// Validate and build FilterRel
	...
}Validations could still be performed at build time, but an API like this would allow us to:
- Make relation structs fully immutable, instead of half-and-half.
 - Allow users the ability to fully-customize a relation without having to add a new method to the 
BuilderorRelinterface. 
This would effectively be a very low-level API, but we could build other APIs on top of it.