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

Adds longest prefix matching for custom indexes. #20

Merged
merged 3 commits into from
Mar 1, 2016

Conversation

slackpad
Copy link
Contributor

@slackpad slackpad commented Mar 1, 2016

This adds support for longest prefix matching as needed by hashicorp/consul#1764 (it's not yet integrated over there).

One unfortunate thing is that the null suffixes we add with the StringFieldIndex don't work with the algorithm as its currently written - it would need to see if there's a null edge and use that in order to terminate properly. The LongestPrefix algorithm is down in the immutable radix tree, so there's no super clean way to get it to understand this.

For my application, I need a custom indexer, similar to the one in the unit test here that can index an empty string, and that will only get added for query templates, so this isn't a big deal. I commented LongestPrefix with details about this limitation, and I also made it look for common misconfigurations that will make it not work as expected.

I think we could make under-the-hood improvements to remove this limitation with the StringFieldIndex in the future, but this interface would still be useful (it's the LongestPrefix form of First).

@slackpad
Copy link
Contributor Author

slackpad commented Mar 1, 2016

/cc @armon or @dadgar for review

@slackpad slackpad changed the title Adds longest prefix matching with custom indexes. Adds longest prefix matching for custom indexes. Mar 1, 2016
t.Fatalf("err: %v", err)
}
if raw != obj1 {
t.Fatalf("should be nil")
Copy link
Member

Choose a reason for hiding this comment

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

But should it? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

aw - cut n' paste - sorry!

@@ -334,6 +334,39 @@ func (txn *Txn) First(table, index string, args ...interface{}) (interface{}, er
return value, nil
}

// LongestPrefix is used to fetch the longest prefix match for the given
// constraints on the index. Note that this will not work with the memdb
// StringFieldIndex because it adds null terminators which prevent the
Copy link
Member

Choose a reason for hiding this comment

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

Would it be possible to make a call to PrefixFromArgs() to strip the \x00? Since this only supports prefix indexers, seems like that should be possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ryanuber I thought that, too. You can do that to strip the \x00 off the prefix, but when the LongestPrefix routine looks in the tree it hits the node above the actual entry in the index, so it doesn't report anything since that node isn't a leaf. Here's an example:

Say you have foo in your tree with a string field indexer. That'll be in there as foo\x00. When you do this algorithm for foobar it'll scan for the common prefix foo just fine and end up at a node with a prefix of foo and an edge to \x00 which is the proper leaf. It doesn't know that it should follow that edge, though, so it just says that there's no entry in there. Because we always put a suffix in there for the string indexer it always runs into this. If you knew to look up foo\x00 it would work, but you don't know what the longest prefix is when you do a query.

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 the right fix would be to stop adding the \x00 for the string indexer and instead put that into the compound indexer, because that's where it really matters. That's a bigger change though, and doing that wouldn't affect this interface, just the cautionary comment :-)

@ryanuber
Copy link
Member

ryanuber commented Mar 1, 2016

@slackpad Left some comments and a question. Code looks great, just curious about the StringFieldIndex. See the question inline.

func (txn *Txn) LongestPrefix(table, index string, args ...interface{}) (interface{}, error) {
// Enforce that this only works on prefix indexes.
if !strings.HasSuffix(index, "_prefix") {
return nil, fmt.Errorf("index '%s' does not support prefix lookups", index)
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure I would say does not support prefix lookups because they could pass an index that supports it but didn't append "_prefix". Maybe just "must use prefix lookup on index"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call - I'll change that.

@dadgar
Copy link
Contributor

dadgar commented Mar 1, 2016

LGTM

slackpad added a commit that referenced this pull request Mar 1, 2016
Adds longest prefix matching for custom indexes.
@slackpad slackpad merged commit 59c0fed into master Mar 1, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants