Skip to content
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

feat: Enable secondary index for compound filter conditions #3417

Open
wants to merge 70 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 67 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
32c2440
Add json traversal functions
islamaliev Nov 24, 2024
53cb4cb
Add GetPath method to JSON
islamaliev Nov 25, 2024
f703e3a
Include array element index in path
islamaliev Nov 25, 2024
403c587
Fix json traversal
islamaliev Nov 28, 2024
1c059fb
Add JSON and Bool encoding
islamaliev Nov 28, 2024
c784f61
Correctly handle paths to json nodes
islamaliev Nov 29, 2024
9855f30
Base JSON index implementation
islamaliev Nov 30, 2024
0faa02b
Move match-related code to a file
islamaliev Dec 1, 2024
92f7958
Make index work for bool and string
islamaliev Dec 1, 2024
516d290
Add filter by json null value
islamaliev Dec 1, 2024
af5eba2
Add MD file for secondary indexes
islamaliev Dec 1, 2024
3dcb838
Add note about indexing of related docs
islamaliev Dec 2, 2024
e27d5db
Add note about json indexing
islamaliev Dec 2, 2024
7e00694
Enable filtering by json bool and string
islamaliev Dec 3, 2024
40341a0
Add unique json index
islamaliev Dec 6, 2024
61a7b90
Filter by array elements
islamaliev Dec 8, 2024
32ef7bc
Fix _in/_nin filter for json docs
islamaliev Dec 8, 2024
fc0eb2b
Add filtering on arrays of json docs
islamaliev Dec 9, 2024
70f8651
Remove filtering without array elements
islamaliev Dec 10, 2024
cdb9d34
Add tests for composite index with json
islamaliev Dec 14, 2024
adb71d4
Enable indexing of array within json docs
islamaliev Dec 16, 2024
bb67d2f
Enable json array traversal to only top level elements
islamaliev Dec 16, 2024
8f24c04
Fix lint
islamaliev Dec 16, 2024
279bb69
Update docs
islamaliev Dec 16, 2024
343f5fc
Fix test expectations
islamaliev Dec 16, 2024
a56d3bf
Add change detector note
islamaliev Dec 16, 2024
b31c6c0
Polish
islamaliev Dec 17, 2024
69a429b
Update documentation
islamaliev Dec 20, 2024
74605db
Update documentation
islamaliev Jan 2, 2025
efef1b1
Rename
islamaliev Jan 2, 2025
85b5e50
Update documentation
islamaliev Jan 2, 2025
c181d9e
Add encoding/decoding tests
islamaliev Jan 2, 2025
669cc85
Fix import
islamaliev Jan 2, 2025
122bc24
Improve coverage
islamaliev Jan 2, 2025
8308672
PR fixup
islamaliev Jan 2, 2025
f237f2f
Update copyright
islamaliev Jan 2, 2025
575a469
Merge remote-tracking branch 'upstream/develop' into feat/sec-index-o…
islamaliev Jan 7, 2025
edb8386
Add json array path encoding
islamaliev Jan 7, 2025
b6c6da5
follow up change
islamaliev Jan 9, 2025
1908a18
Merge remote-tracking branch 'upstream/develop' into feat/sec-index-o…
islamaliev Jan 9, 2025
ab54595
Add filter traversal
islamaliev Jan 10, 2025
759b987
Use filter traversal
islamaliev Jan 11, 2025
507ad20
Scope _none to only arrays
islamaliev Jan 11, 2025
5bd11d8
Merge remote-tracking branch 'upstream/develop' into feat/json-array-…
islamaliev Jan 13, 2025
10d3e30
Add a note for data change
islamaliev Jan 13, 2025
ebe52a5
Merge branch 'feat/json-array-scoped-search' into feat/make-index-che…
islamaliev Jan 13, 2025
a9ddfc5
improve filter condition traversal
islamaliev Jan 14, 2025
e6153ce
Add comments
islamaliev Jan 14, 2025
213bba5
Merge remote-tracking branch 'upstream/develop' into feat/json-array-…
islamaliev Jan 14, 2025
19fc278
Merge branch 'feat/json-array-scoped-search' into feat/make-index-che…
islamaliev Jan 15, 2025
d0dca50
Add ability to skip ops when traversing
islamaliev Jan 16, 2025
60ed8a3
Merge remote-tracking branch 'upstream/develop' into feat/make-index-…
islamaliev Jan 21, 2025
bf2e68d
Make connor check if prop exists for _ne
islamaliev Jan 26, 2025
52b2451
Make filter traverse validate some ops values
islamaliev Jan 26, 2025
ff3c7b1
Make index fetcher implement new interface
islamaliev Jan 26, 2025
7d1ddf0
Adjust tests
islamaliev Jan 26, 2025
cf86ebe
Merge remote-tracking branch 'upstream/develop' into feat/make-index-…
islamaliev Jan 26, 2025
158f364
Merge remote-tracking branch 'upstream/develop' into feat/make-index-…
islamaliev Jan 28, 2025
3e79a98
Fix issues
islamaliev Jan 29, 2025
818d3fe
Add test issue
islamaliev Jan 29, 2025
0f278b1
Adjust explain metrics
islamaliev Jan 30, 2025
12abf5c
Polish
islamaliev Jan 30, 2025
0394f13
Fix lint
islamaliev Jan 30, 2025
dd307fd
Adjust tests
islamaliev Jan 31, 2025
e9ffb04
Add note for change detector
islamaliev Jan 31, 2025
ca30e90
Adjust tests
islamaliev Jan 31, 2025
b33415c
Adjust tests
islamaliev Jan 31, 2025
a56180c
PR fixup
islamaliev Feb 4, 2025
cdaf7c4
Merge remote-tracking branch 'upstream/develop' into feat/make-index-…
islamaliev Feb 4, 2025
91ac5a9
Fix lint
islamaliev Feb 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Enabled compound filter conditions for secondary indexes

Changed the way fieldFetched are counted for explain directive
2 changes: 1 addition & 1 deletion internal/connor/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func all(condition, data any) (bool, error) {

func allSlice[T any](condition any, data []T) (bool, error) {
for _, c := range data {
m, err := eq(condition, c)
m, err := eq(condition, c, true)
if err != nil {
return false, err
} else if !m {
Expand Down
2 changes: 1 addition & 1 deletion internal/connor/and.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ func and(condition, data any) (bool, error) {
switch cn := condition.(type) {
case []any:
for _, c := range cn {
if m, err := eq(c, data); err != nil {
if m, err := eq(c, data, true); err != nil {
return false, err
} else if !m {
return false, nil
Expand Down
2 changes: 1 addition & 1 deletion internal/connor/any.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func anyOp(condition, data any) (bool, error) {

func anySlice[T any](condition any, data []T) (bool, error) {
for _, c := range data {
m, err := eq(condition, c)
m, err := eq(condition, c, true)
if err != nil {
return false, err
} else if m {
Expand Down
8 changes: 4 additions & 4 deletions internal/connor/connor.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ func IsOpSimple(op string) bool {
// Match is the default method used in Connor to match some data to a
// set of conditions.
func Match(conditions map[FilterKey]any, data any) (bool, error) {
return eq(conditions, data)
return eq(conditions, data, true)
}

// matchWith can be used to specify the exact operator to use when performing
// a match operation. This is primarily used when building custom operators or
// if you wish to override the behavior of another operator.
func matchWith(op string, conditions, data any) (bool, error) {
func matchWith(op string, conditions, data any, propExists bool) (bool, error) {
islamaliev marked this conversation as resolved.
Show resolved Hide resolved
switch op {
case AndOp:
return and(conditions, data)
Expand All @@ -64,7 +64,7 @@ func matchWith(op string, conditions, data any) (bool, error) {
case AllOp:
return all(conditions, data)
case EqualOp, AliasOp:
return eq(conditions, data)
return eq(conditions, data, propExists)
case GreaterOrEqualOp:
return ge(conditions, data)
case GreaterOp:
Expand All @@ -76,7 +76,7 @@ func matchWith(op string, conditions, data any) (bool, error) {
case LesserOp:
return lt(conditions, data)
case NotEqualOp:
return ne(conditions, data)
return ne(conditions, data, propExists)
case NotInOp:
return nin(conditions, data)
case OrOp:
Expand Down
12 changes: 6 additions & 6 deletions internal/connor/eq.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

// eq is an operator which performs object equality
// tests.
func eq(condition, data any) (bool, error) {
func eq(condition, data any, propExists bool) (bool, error) {
islamaliev marked this conversation as resolved.
Show resolved Hide resolved
switch arr := data.(type) {
case []core.Doc:
return anySlice(condition, arr)
Expand All @@ -34,11 +34,11 @@ func eq(condition, data any) (bool, error) {
switch cn := condition.(type) {
case map[FilterKey]any:
for prop, cond := range cn {
d, op, err := prop.PropertyAndOperator(data, EqualOp)
if err != nil {
return false, err
res := prop.PropertyAndOperator(data, EqualOp)
if res.Err != nil {
return false, res.Err
}
m, err := matchWith(op, cond, d)
m, err := matchWith(res.Operator, cond, res.Data, !res.MissProp && propExists)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -85,7 +85,7 @@ func objectsEqual(condition map[string]any, data any) (bool, error) {
return false, nil
}
for k, v := range d {
m, err := eq(condition[k], v)
m, err := eq(condition[k], v, true)
if err != nil {
return false, err
} else if !m {
Expand Down
2 changes: 1 addition & 1 deletion internal/connor/in.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ func in(conditions, data any) (bool, error) {
switch cn := conditions.(type) {
case []any:
for _, ce := range cn {
if m, err := eq(ce, data); err != nil {
if m, err := eq(ce, data, true); err != nil {
return false, err
} else if m {
return true, nil
Expand Down
22 changes: 17 additions & 5 deletions internal/connor/key.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
package connor

// KeyResult represents the result of a filter key operation.
type KeyResult struct {
// Data is the data that should be used to filter the value matching the key.
Data any
// MissProp is true if the key is missing a property, otherwise false.
// It's relevant for object of dynamic type, like JSON.
MissProp bool
// Operator is the operator that should be used to filter the value matching the key.
// If the key does not have an operator the given defaultOp will be returned.
Operator string
// Err is the error that occurred while filtering the value matching the key.
Err error
}

// FilterKey represents a type that may be used as a map key
// in a filter.
type FilterKey interface {
// PropertyAndOperator returns the data and operator that should be used
// to filter the value matching this key.
//
// If the key does not have an operator the given defaultOp will be returned.
PropertyAndOperator(data any, defaultOp string) (any, string, error)
// PropertyAndOperator returns [KeyResult] that contains data and operator that should be
// used to filter the value matching this key.
PropertyAndOperator(data any, defaultOp string) KeyResult
islamaliev marked this conversation as resolved.
Show resolved Hide resolved
// Equal returns true if other is equal, otherwise returns false.
Equal(other FilterKey) bool
}
9 changes: 7 additions & 2 deletions internal/connor/ne.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ package connor

// ne performs object inequality comparisons by inverting
// the result of the EqualOperator for non-error cases.
func ne(conditions, data any) (bool, error) {
m, err := eq(conditions, data)
func ne(conditions, data any, propExists bool) (bool, error) {
// _ne operator should return false if the property does not exist.
if !propExists {
return false, nil
}

m, err := eq(conditions, data, propExists)

if err != nil {
return false, err
Expand Down
2 changes: 1 addition & 1 deletion internal/connor/none.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func none(condition, data any) (bool, error) {

func noneSlice[T any](condition any, data []T) (bool, error) {
for _, c := range data {
m, err := eq(condition, c)
m, err := eq(condition, c, true)
if err != nil {
return false, err
} else if m {
Expand Down
2 changes: 1 addition & 1 deletion internal/connor/not.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package connor
// not is an operator which performs object equality test
// and returns the inverse of the result.
func not(condition, data any) (bool, error) {
m, err := eq(condition, data)
m, err := eq(condition, data, true)
if err != nil {
return false, err
}
Expand Down
4 changes: 2 additions & 2 deletions internal/connor/not_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ type operator struct {
Operation string
}

func (k *operator) PropertyAndOperator(data any, defaultOp string) (any, string, error) {
return data, k.Operation, nil
func (k *operator) PropertyAndOperator(data any, defaultOp string) KeyResult {
return KeyResult{Data: data, Operator: k.Operation}
}

func (k *operator) Equal(other FilterKey) bool {
Expand Down
2 changes: 1 addition & 1 deletion internal/connor/or.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ func or(condition, data any) (bool, error) {
switch cn := condition.(type) {
case []any:
for _, c := range cn {
if m, err := eq(c, data); err != nil {
if m, err := eq(c, data, true); err != nil {
return false, err
} else if m {
return true, nil
Expand Down
2 changes: 1 addition & 1 deletion internal/db/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (c *collection) newFetcher() fetcher.Fetcher {
if c.fetcherFactory != nil {
innerFetcher = c.fetcherFactory()
} else {
innerFetcher = fetcher.NewDocumentFetcher()
innerFetcher = fetcher.NewDocumentFetcher(immutable.Option[client.IndexDescription]{})
islamaliev marked this conversation as resolved.
Show resolved Hide resolved
}

return lens.NewFetcher(innerFetcher, c.db.LensRegistry())
Expand Down
10 changes: 8 additions & 2 deletions internal/db/fetcher/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@

for {
res, ok := f.kvResultsIter.NextSync()
if res.Error != nil {
return immutable.None[EncodedDocument](), res.Error
}

Check warning on line 140 in internal/db/fetcher/document.go

View check run for this annotation

Codecov / codecov/patch

internal/db/fetcher/document.go#L139-L140

Added lines #L139 - L140 were not covered by tests
if !ok {
break
}
Expand Down Expand Up @@ -179,13 +182,16 @@
return err
}

// we count the fields fetched here instead of after checking if the field was requested
// because we need to count all fields fetched to see more accurate picture of the performance
// of the query
f.execInfo.FieldsFetched++

fieldDesc, ok := f.fieldsByID[fieldID]
if !ok {
return nil
}

f.execInfo.FieldsFetched++

doc.properties[fieldDesc] = &encProperty{
Desc: fieldDesc,
Raw: kv.Value,
Expand Down
Loading
Loading