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: Secondary index reader #829

Merged
merged 6 commits into from
Mar 1, 2023
Merged

feat: Secondary index reader #829

merged 6 commits into from
Mar 1, 2023

Conversation

garrensmith
Copy link
Contributor

Describe your changes

Secondary Index reader that queries from the secondary index.

How best to test these changes

Issue ticket number and link

@reviewpad reviewpad bot added the large label Feb 16, 2023
@garrensmith garrensmith added debug image This PR will generate a debug image and removed large labels Feb 16, 2023
@reviewpad reviewpad bot added the large label Feb 16, 2023
}
}

// TODO: I think this might need to be changed, we should only support search on the search endpoint
if options.filter.None() || !options.filter.IsIndexed() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still support search as a reader option or should it only be part of the search API?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reader option is internal to us but to answer your question, yes we need to support it till we backfill the secondary indexes and point the read queries to start using secondary indexes. Then it will only be Search.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created issue #869 for this

@@ -226,8 +226,8 @@ var DefaultConfig = Config{
WriteEnabled: true,
},
SecondaryIndex: SecondaryIndexConfig{
ReadEnabled: false,
WriteEnabled: false,
ReadEnabled: true,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's not enable it in the config yet.

}
}

// TODO: I think this might need to be changed, we should only support search on the search endpoint
if options.filter.None() || !options.filter.IsIndexed() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reader option is internal to us but to answer your question, yes we need to support it till we backfill the secondary indexes and point the read queries to start using secondary indexes. Then it will only be Search.

eqPlan, err := eqKeyBuilder.Build(queryFilters, coll.QueryableFields)
if err == nil {
for _, plan := range eqPlan {
if indexedDataType(plan) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it is equality and the plan is not index type what is the behavior?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can happen if the field is a []byte. Then the plan is ignored and we look for another eq plan or move to range plans

@@ -121,56 +226,176 @@ func (s *StrictEqKeyComposer) Compose(selectors []*Selector, userDefinedKeys []*
}

if len(repeatedFields) == 0 {
// nothing found or a gap
return nil, errors.InvalidArgument("filters doesn't contains primary key fields")
if s.matchAll {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this will go away once we move to secondary indexes because then it doesn't matter if we are able to build the primary key from the filter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that will be awesome. I've created #870 to remember to do this.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great

}

// Prefer a RANGE scan which should read back less keys than a FULLRANGE
// for _, queryType := range []filter.QueryPlanType{filter.RANGE, filter.FULLRANGE} {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove it if not needed.

// Range Key Composer will generate a range key set on the user defined keys
// It will set the KeyQuery to `FullRange` if the start or end key is not defined in the query
// if there is a defined start and end key for a range then `Range` is set.
type RangeKeyComposer[F fieldable] struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this diff, the structure is good. But if we think about it, we just need two high level abstractions here,

  • NewQueryableKeyBuilder(composer, primaryKey) [will be used by insert/replace only]
  • NewQueryableKeyBuilder(): This will internally use eq/range composer to build plans i.e. equality as well as range. The reason being once we don't rely on the search store then either the plan is formed or we need to fall back to full table scan, there will be no other option. Then Read/Update/Delete can rely directly on it. This means we need to add something to the QueryPlan so that caller knows whether the keys are for primary or secondary indexes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I like it. We can definitely move to that direction once search and secondary are ready.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sg

@garrensmith garrensmith changed the title [WIP]Secondary reader Secondary reader Feb 23, 2023
Copy link
Contributor Author

@garrensmith garrensmith left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. I've made the changes.

}

// NewPrimaryKeyEQBuild returns a KeyBuilder for use with schema.Field to build a primary key query plan.
func NewPrimaryKeyEqBuilder(keyEncodingFunc KeyEncodingFunc) *KeyBuilder[*schema.Field] {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could do that a little later once secondary indexes are deployed. At the moment we have to pass some extra params if it is a PrimaryKey versus secondary.

@@ -121,56 +226,176 @@ func (s *StrictEqKeyComposer) Compose(selectors []*Selector, userDefinedKeys []*
}

if len(repeatedFields) == 0 {
// nothing found or a gap
return nil, errors.InvalidArgument("filters doesn't contains primary key fields")
if s.matchAll {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that will be awesome. I've created #870 to remember to do this.

// Range Key Composer will generate a range key set on the user defined keys
// It will set the KeyQuery to `FullRange` if the start or end key is not defined in the query
// if there is a defined start and end key for a range then `Range` is set.
type RangeKeyComposer[F fieldable] struct {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I like it. We can definitely move to that direction once search and secondary are ready.

eqPlan, err := eqKeyBuilder.Build(queryFilters, coll.QueryableFields)
if err == nil {
for _, plan := range eqPlan {
if indexedDataType(plan) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can happen if the field is a []byte. Then the plan is ignored and we look for another eq plan or move to range plans

@@ -303,6 +316,12 @@ func buildValueMatcher(input jsoniter.RawMessage, field *schema.QueryableField)
return nil, nil, err
}
collation = value.NewCollationFrom(apiCollation)

if buildForSecondaryIndex && collation.IsCaseInsensitive() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we won't be supporting case-insensitive queries on secondary indexes fields?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can support it in the in-memory filter part but not part of what we use to build the range keys. Or at least I can't think of a way of making that work.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, we can figure it out iteratively.

@garrensmith garrensmith force-pushed the secondary-reader branch 3 times, most recently from 0896d17 to 74869e7 Compare March 1, 2023 05:54
himank
himank previously approved these changes Mar 1, 2023
@himank himank changed the title Secondary reader feat: Secondary index reader Mar 1, 2023
@garrensmith garrensmith merged commit 40f2fdd into main Mar 1, 2023
@garrensmith garrensmith deleted the secondary-reader branch March 1, 2023 10:39
@tigrisdata-argocd-bot
Copy link
Collaborator

🎉 This PR is included in version 1.0.0-beta.46 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
debug image This PR will generate a debug image large released on @beta
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants