mirror of
https://github.com/anyproto/anytype-heart.git
synced 2025-06-11 18:20:33 +09:00
GO-2743: add filters
Signed-off-by: AnastasiaShemyakinskaya <shem98a@mail.ru>
This commit is contained in:
parent
c01186f174
commit
ed86d4c654
7 changed files with 173 additions and 19 deletions
|
@ -240,7 +240,7 @@ func (mw *Middleware) enrichWithDateSuggestion(ctx context.Context, records []da
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("make date record: %w", err)
|
||||
}
|
||||
f, _ := database.MakeFiltersAnd(req.Filters, store) //nolint:errcheck
|
||||
f, _ := database.MakeFilters(req.Filters, store) //nolint:errcheck
|
||||
if f.FilterObject(rec.Details) {
|
||||
return append([]database.Record{rec}, records...), nil
|
||||
}
|
||||
|
|
|
@ -96,6 +96,110 @@ func TestService_Search(t *testing.T) {
|
|||
|
||||
assert.NoError(t, fx.Unsubscribe("test"))
|
||||
assert.Len(t, fx.Service.(*service).cache.entries, 0)
|
||||
})
|
||||
t.Run("search with filters: one filter None", func(t *testing.T) {
|
||||
fx := newFixtureWithRealObjectStore(t)
|
||||
defer fx.a.Close(context.Background())
|
||||
defer fx.ctrl.Finish()
|
||||
source := "source"
|
||||
spaceID := "spaceId"
|
||||
relationKey := "key"
|
||||
option1 := "option1"
|
||||
option2 := "option2"
|
||||
|
||||
defer fx.a.Close(context.Background())
|
||||
defer fx.ctrl.Finish()
|
||||
objectTypeKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeObjectType, source)
|
||||
assert.Nil(t, err)
|
||||
|
||||
relationUniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeRelation, relationKey)
|
||||
assert.Nil(t, err)
|
||||
|
||||
option1UniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeRelationOption, option1)
|
||||
assert.Nil(t, err)
|
||||
|
||||
option2UniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeRelationOption, option2)
|
||||
assert.Nil(t, err)
|
||||
|
||||
fx.store.AddObjects(t, []objectstore.TestObject{
|
||||
{
|
||||
bundle.RelationKeyId: pbtypes.String(relationKey),
|
||||
bundle.RelationKeyUniqueKey: pbtypes.String(relationUniqueKey.Marshal()),
|
||||
bundle.RelationKeySpaceId: pbtypes.String(spaceID),
|
||||
bundle.RelationKeyRelationFormat: pbtypes.Int64(int64(model.RelationFormat_status)),
|
||||
bundle.RelationKeyLayout: pbtypes.Int64(int64(model.ObjectType_relation)),
|
||||
},
|
||||
{
|
||||
bundle.RelationKeyId: pbtypes.String(source),
|
||||
bundle.RelationKeyUniqueKey: pbtypes.String(objectTypeKey.Marshal()),
|
||||
bundle.RelationKeySpaceId: pbtypes.String(spaceID),
|
||||
bundle.RelationKeyLayout: pbtypes.Int64(int64(model.ObjectType_objectType)),
|
||||
},
|
||||
{
|
||||
bundle.RelationKeyId: pbtypes.String(option1),
|
||||
bundle.RelationKeySpaceId: pbtypes.String(spaceID),
|
||||
bundle.RelationKeyRelationKey: pbtypes.String(relationKey),
|
||||
bundle.RelationKeyLayout: pbtypes.Int64(int64(model.ObjectType_relationOption)),
|
||||
bundle.RelationKeyName: pbtypes.String("Done"),
|
||||
bundle.RelationKeyUniqueKey: pbtypes.String(option1UniqueKey.Marshal()),
|
||||
},
|
||||
{
|
||||
bundle.RelationKeyId: pbtypes.String(option2),
|
||||
bundle.RelationKeySpaceId: pbtypes.String(spaceID),
|
||||
bundle.RelationKeyRelationKey: pbtypes.String(relationKey),
|
||||
bundle.RelationKeyLayout: pbtypes.Int64(int64(model.ObjectType_relationOption)),
|
||||
bundle.RelationKeyName: pbtypes.String("Not started"),
|
||||
bundle.RelationKeyUniqueKey: pbtypes.String(option2UniqueKey.Marshal()),
|
||||
},
|
||||
{
|
||||
bundle.RelationKeyId: pbtypes.String("1"),
|
||||
bundle.RelationKeySpaceId: pbtypes.String(spaceID),
|
||||
domain.RelationKey(relationKey): pbtypes.String(option1UniqueKey.Marshal()),
|
||||
bundle.RelationKeyLayout: pbtypes.Int64(int64(model.ObjectType_basic)),
|
||||
bundle.RelationKeyName: pbtypes.String("Object 1"),
|
||||
bundle.RelationKeyType: pbtypes.String(objectTypeKey.Marshal()),
|
||||
},
|
||||
{
|
||||
bundle.RelationKeyId: pbtypes.String("2"),
|
||||
bundle.RelationKeySpaceId: pbtypes.String(spaceID),
|
||||
domain.RelationKey(relationKey): pbtypes.String(option2UniqueKey.Marshal()),
|
||||
bundle.RelationKeyLayout: pbtypes.Int64(int64(model.ObjectType_basic)),
|
||||
bundle.RelationKeyName: pbtypes.String("Object 2"),
|
||||
bundle.RelationKeyType: pbtypes.String(objectTypeKey.Marshal()),
|
||||
},
|
||||
})
|
||||
resp, err := fx.Search(pb.RpcObjectSearchSubscribeRequest{
|
||||
Keys: []string{bundle.RelationKeyId.String()},
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: 0,
|
||||
RelationKey: relationKey,
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(option1),
|
||||
Format: model.RelationFormat_status,
|
||||
},
|
||||
},
|
||||
NoDepSubscription: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, resp.Records, 1)
|
||||
assert.Equal(t, option1, resp.Records[0].Fields[bundle.RelationKeyId.String()].GetStringValue())
|
||||
})
|
||||
t.Run("search with filters: linear structure with none filters", func(t *testing.T) {
|
||||
|
||||
})
|
||||
t.Run("search with filters: tree structure with And filter in root and None filters in NesterFilters", func(t *testing.T) {
|
||||
|
||||
})
|
||||
t.Run("search with filters: tree structure with Or filter in root and None filters in NesterFilters", func(t *testing.T) {
|
||||
|
||||
})
|
||||
t.Run("search with filters: tree structure with And filter in root and combined filters as NestedFilter", func(t *testing.T) {
|
||||
|
||||
})
|
||||
t.Run("search with filters: tree structure with Or filter in root and combined filters as NestedFilter", func(t *testing.T) {
|
||||
|
||||
})
|
||||
t.Run("cache ref counter", func(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
|
|
|
@ -26310,6 +26310,7 @@ Bookmark is to keep a web-link and to preview a content.
|
|||
| quickOption | [Block.Content.Dataview.Filter.QuickOption](#anytype-model-Block-Content-Dataview-Filter-QuickOption) | | |
|
||||
| format | [RelationFormat](#anytype-model-RelationFormat) | | |
|
||||
| includeTime | [bool](#bool) | | |
|
||||
| nestedFilters | [Block.Content.Dataview.Filter](#anytype-model-Block-Content-Dataview-Filter) | repeated | |
|
||||
|
||||
|
||||
|
||||
|
@ -27786,8 +27787,9 @@ stored |
|
|||
|
||||
| Name | Number | Description |
|
||||
| ---- | ------ | ----------- |
|
||||
| And | 0 | |
|
||||
| No | 0 | |
|
||||
| Or | 1 | |
|
||||
| And | 2 | |
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ func NewFilters(qry Query, store ObjectStore, arena *fastjson.Arena) (filters *F
|
|||
objectStore: store,
|
||||
}
|
||||
|
||||
filterObj, err := MakeFiltersAnd(qry.Filters, store)
|
||||
filterObj, err := MakeFilters(qry.Filters, store)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -23,26 +23,72 @@ var (
|
|||
ErrValueMustBeListSupporting = errors.New("value must be list supporting")
|
||||
)
|
||||
|
||||
func MakeFiltersAnd(protoFilters []*model.BlockContentDataviewFilter, store ObjectStore) (FiltersAnd, error) {
|
||||
func MakeFilters(protoFilters []*model.BlockContentDataviewFilter, store ObjectStore) (Filter, error) {
|
||||
if store == nil {
|
||||
return FiltersAnd{}, fmt.Errorf("objectStore dependency is nil")
|
||||
}
|
||||
spaceID := getSpaceIDFromFilters(protoFilters)
|
||||
protoFilters = TransformQuickOption(protoFilters, nil)
|
||||
if len(protoFilters) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
spaceID := getSpaceIDFromFilters(protoFilters)
|
||||
operator := protoFilters[0].Operator
|
||||
filter, err := makeFilters(protoFilters, store, operator, spaceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return filter, nil
|
||||
}
|
||||
|
||||
func makeFilters(protoFilters []*model.BlockContentDataviewFilter,
|
||||
store ObjectStore,
|
||||
operator model.BlockContentDataviewFilterOperator,
|
||||
spaceID string,
|
||||
) (Filter, error) {
|
||||
switch operator {
|
||||
case model.BlockContentDataviewFilter_No:
|
||||
filter, err := MakeFilter(spaceID, protoFilters[0], store)
|
||||
return filter, err
|
||||
case model.BlockContentDataviewFilter_Or:
|
||||
return makeFilterOr(protoFilters, store, spaceID)
|
||||
case model.BlockContentDataviewFilter_And:
|
||||
return makeFilterAnd(protoFilters, store, spaceID)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func makeFilterAnd(filters []*model.BlockContentDataviewFilter, store ObjectStore, spaceId string) (Filter, error) {
|
||||
var and FiltersAnd
|
||||
for _, pf := range protoFilters {
|
||||
if pf.Condition != model.BlockContentDataviewFilter_None {
|
||||
f, err := MakeFilter(spaceID, pf, store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
and = append(and, f)
|
||||
for _, filter := range filters {
|
||||
if len(filter.NestedFilters) == 0 {
|
||||
return MakeFilter(spaceId, filter, store)
|
||||
}
|
||||
ns := TransformQuickOption(filter.NestedFilters, nil)
|
||||
f, err := makeFilters(ns, store, filter.Operator, spaceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
and = append(and, f)
|
||||
}
|
||||
return and, nil
|
||||
}
|
||||
|
||||
func makeFilterOr(filters []*model.BlockContentDataviewFilter, store ObjectStore, spaceId string) (Filter, error) {
|
||||
var or FiltersOr
|
||||
for _, filter := range filters {
|
||||
if len(filter.NestedFilters) == 0 {
|
||||
return MakeFilter(spaceId, filter, store)
|
||||
}
|
||||
ns := TransformQuickOption(filter.NestedFilters, nil)
|
||||
f, err := makeFilters(ns, store, filter.Operator, spaceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
or = append(or, f)
|
||||
}
|
||||
return or, nil
|
||||
}
|
||||
|
||||
func NestedRelationKey(baseRelationKey domain.RelationKey, nestedRelationKey domain.RelationKey) string {
|
||||
return fmt.Sprintf("%s.%s", baseRelationKey.String(), nestedRelationKey.String())
|
||||
}
|
||||
|
|
|
@ -366,7 +366,7 @@ func TestMakeAndFilter(t *testing.T) {
|
|||
Value: pbtypes.StringList([]string{"14"}),
|
||||
},
|
||||
}
|
||||
andFilter, err := MakeFiltersAnd(filters, store)
|
||||
andFilter, err := MakeFilters(filters, store)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, andFilter, 14)
|
||||
})
|
||||
|
@ -377,7 +377,7 @@ func TestMakeAndFilter(t *testing.T) {
|
|||
model.BlockContentDataviewFilter_AllIn,
|
||||
model.BlockContentDataviewFilter_NotAllIn,
|
||||
} {
|
||||
_, err := MakeFiltersAnd([]*model.BlockContentDataviewFilter{
|
||||
_, err := MakeFilters([]*model.BlockContentDataviewFilter{
|
||||
{Condition: cond, Value: pbtypes.Null()},
|
||||
}, store)
|
||||
assert.Equal(t, ErrValueMustBeListSupporting, err)
|
||||
|
@ -385,13 +385,13 @@ func TestMakeAndFilter(t *testing.T) {
|
|||
|
||||
})
|
||||
t.Run("unexpected condition", func(t *testing.T) {
|
||||
_, err := MakeFiltersAnd([]*model.BlockContentDataviewFilter{
|
||||
_, err := MakeFilters([]*model.BlockContentDataviewFilter{
|
||||
{Condition: 10000},
|
||||
}, store)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("replace 'value == false' to 'value != true'", func(t *testing.T) {
|
||||
f, err := MakeFiltersAnd([]*model.BlockContentDataviewFilter{
|
||||
f, err := MakeFilters([]*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: "b",
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
|
@ -410,7 +410,7 @@ func TestMakeAndFilter(t *testing.T) {
|
|||
assertFilter(t, f, g, false)
|
||||
})
|
||||
t.Run("replace 'value != false' to 'value == true'", func(t *testing.T) {
|
||||
f, err := MakeFiltersAnd([]*model.BlockContentDataviewFilter{
|
||||
f, err := MakeFilters([]*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: "b",
|
||||
Condition: model.BlockContentDataviewFilter_NotEqual,
|
||||
|
|
|
@ -432,10 +432,12 @@ message Block {
|
|||
QuickOption quickOption = 6;
|
||||
RelationFormat format = 7;
|
||||
bool includeTime = 8;
|
||||
repeated Filter nestedFilters = 10;
|
||||
|
||||
enum Operator {
|
||||
And = 0;
|
||||
No = 0;
|
||||
Or = 1;
|
||||
And = 2;
|
||||
}
|
||||
|
||||
enum Condition {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue