1
0
Fork 0
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:
AnastasiaShemyakinskaya 2024-03-01 12:16:32 +01:00
parent c01186f174
commit ed86d4c654
No known key found for this signature in database
GPG key ID: CCD60ED83B103281
7 changed files with 173 additions and 19 deletions

View file

@ -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
}

View file

@ -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)

View file

@ -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 | |

View file

@ -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
}

View file

@ -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())
}

View file

@ -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,

View file

@ -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 {