mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-13 19:44:21 +00:00
1d84635836
# Which Problems Are Solved To improve performance a new table and method is implemented on eventstore. The goal of this table is to index searchable fields on command side to use it on command and query side. The table allows to store one primitive value (numeric, text) per row. The eventstore framework is extended by the `Search`-method which allows to search for objects. The `Command`-interface is extended by the `SearchOperations()`-method which does manipulate the the `search`-table. # How the Problems Are Solved This PR adds the capability of improving performance for command and query side by using the `Search`-method of the eventstore instead of using one of the `Filter`-methods. # Open Tasks - [x] Add feature flag - [x] Unit tests - [ ] ~~Benchmarks if needed~~ - [x] Ensure no behavior change - [x] Add setup step to fill table with current data - [x] Add projection which ensures data added between setup and start of the new version are also added to the table # Additional Changes The `Search`-method is currently used by `ProjectGrant`-command side. # Additional Context - Closes https://github.com/zitadel/zitadel/issues/8094
141 lines
4.2 KiB
Go
141 lines
4.2 KiB
Go
package eventstore
|
|
|
|
// FieldOperation if the definition of the operation to be executed on the field
|
|
type FieldOperation struct {
|
|
// Set a field in the field table
|
|
// if [SearchField.UpsertConflictFields] are set the field will be updated if the conflict fields match
|
|
// if no [SearchField.UpsertConflictFields] are set the field will be inserted
|
|
Set *Field
|
|
// Remove fields using the map as `AND`ed conditions
|
|
Remove map[FieldType]any
|
|
}
|
|
|
|
type SearchResult struct {
|
|
Aggregate Aggregate
|
|
Object Object
|
|
FieldName string
|
|
// Value represents the stored value
|
|
// use the Unmarshal method to parse the value to the desired type
|
|
Value interface {
|
|
// Unmarshal parses the value to ptr
|
|
Unmarshal(ptr any) error
|
|
}
|
|
}
|
|
|
|
// // NumericResultValue marshals the value to the given type
|
|
|
|
type Object struct {
|
|
// Type of the object
|
|
Type string
|
|
// ID of the object
|
|
ID string
|
|
// Revision of the object, if an object evolves the revision should be increased
|
|
// analog to current projection versioning
|
|
Revision uint8
|
|
}
|
|
|
|
type Field struct {
|
|
Aggregate *Aggregate
|
|
Object Object
|
|
UpsertConflictFields []FieldType
|
|
FieldName string
|
|
Value Value
|
|
}
|
|
|
|
type Value struct {
|
|
Value any
|
|
// MustBeUnique defines if the field must be unique
|
|
// This field will replace unique constraints in the future
|
|
// If MustBeUnique is true the value must be a primitive type
|
|
MustBeUnique bool
|
|
// ShouldIndex defines if the field should be indexed
|
|
// If the field is not indexed it can not be used in search queries
|
|
// If ShouldIndex is true the value must be a primitive type
|
|
ShouldIndex bool
|
|
}
|
|
|
|
type SearchValueType int8
|
|
|
|
const (
|
|
SearchValueTypeString SearchValueType = iota
|
|
SearchValueTypeNumeric
|
|
)
|
|
|
|
// SetSearchField sets the field based on the defined parameters
|
|
// if conflictFields are set the field will be updated if the conflict fields match
|
|
func SetField(aggregate *Aggregate, object Object, fieldName string, value *Value, conflictFields ...FieldType) *FieldOperation {
|
|
return &FieldOperation{
|
|
Set: &Field{
|
|
Aggregate: aggregate,
|
|
Object: object,
|
|
UpsertConflictFields: conflictFields,
|
|
FieldName: fieldName,
|
|
Value: *value,
|
|
},
|
|
}
|
|
}
|
|
|
|
// RemoveSearchFields removes fields using the map as `AND`ed conditions
|
|
func RemoveSearchFields(clause map[FieldType]any) *FieldOperation {
|
|
return &FieldOperation{
|
|
Remove: clause,
|
|
}
|
|
}
|
|
|
|
// RemoveSearchFieldsByAggregate removes fields using the aggregate as `AND`ed conditions
|
|
func RemoveSearchFieldsByAggregate(aggregate *Aggregate) *FieldOperation {
|
|
return &FieldOperation{
|
|
Remove: map[FieldType]any{
|
|
FieldTypeInstanceID: aggregate.InstanceID,
|
|
FieldTypeResourceOwner: aggregate.ResourceOwner,
|
|
FieldTypeAggregateType: aggregate.Type,
|
|
FieldTypeAggregateID: aggregate.ID,
|
|
},
|
|
}
|
|
}
|
|
|
|
// RemoveSearchFieldsByAggregateAndObject removes fields using the aggregate and object as `AND`ed conditions
|
|
func RemoveSearchFieldsByAggregateAndObject(aggregate *Aggregate, object Object) *FieldOperation {
|
|
return &FieldOperation{
|
|
Remove: map[FieldType]any{
|
|
FieldTypeInstanceID: aggregate.InstanceID,
|
|
FieldTypeResourceOwner: aggregate.ResourceOwner,
|
|
FieldTypeAggregateType: aggregate.Type,
|
|
FieldTypeAggregateID: aggregate.ID,
|
|
FieldTypeObjectType: object.Type,
|
|
FieldTypeObjectID: object.ID,
|
|
FieldTypeObjectRevision: object.Revision,
|
|
},
|
|
}
|
|
}
|
|
|
|
// RemoveSearchFieldsByAggregateAndObjectAndField removes fields using the aggregate, object and field as `AND`ed conditions
|
|
func RemoveSearchFieldsByAggregateAndObjectAndField(aggregate *Aggregate, object Object, field string) *FieldOperation {
|
|
return &FieldOperation{
|
|
Remove: map[FieldType]any{
|
|
FieldTypeInstanceID: aggregate.InstanceID,
|
|
FieldTypeResourceOwner: aggregate.ResourceOwner,
|
|
FieldTypeAggregateType: aggregate.Type,
|
|
FieldTypeAggregateID: aggregate.ID,
|
|
FieldTypeObjectType: object.Type,
|
|
FieldTypeObjectID: object.ID,
|
|
FieldTypeObjectRevision: object.Revision,
|
|
FieldTypeFieldName: field,
|
|
},
|
|
}
|
|
}
|
|
|
|
type FieldType int8
|
|
|
|
const (
|
|
FieldTypeAggregateType FieldType = iota
|
|
FieldTypeAggregateID
|
|
FieldTypeInstanceID
|
|
FieldTypeResourceOwner
|
|
FieldTypeObjectType
|
|
FieldTypeObjectID
|
|
FieldTypeObjectRevision
|
|
FieldTypeFieldName
|
|
FieldTypeValue
|
|
)
|