From 76367813150d1d5e6f7b03578556af71937bff38 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Sat, 26 Oct 2019 10:30:02 +0200 Subject: [PATCH] fix StringIndexOutOfBounds when caret is in position 0 --- .../lang/FindValuesForQueryCompletion.java | 28 ++++++----- .../pdb/datastore/internal/ProposerTest.java | 49 ++++++++++++------- 2 files changed, 49 insertions(+), 28 deletions(-) diff --git a/data-store/src/main/java/org/lucares/pdb/datastore/lang/FindValuesForQueryCompletion.java b/data-store/src/main/java/org/lucares/pdb/datastore/lang/FindValuesForQueryCompletion.java index 796bc3d..a5cc922 100644 --- a/data-store/src/main/java/org/lucares/pdb/datastore/lang/FindValuesForQueryCompletion.java +++ b/data-store/src/main/java/org/lucares/pdb/datastore/lang/FindValuesForQueryCompletion.java @@ -175,21 +175,27 @@ public class FindValuesForQueryCompletion extends ExpressionVisitor visit(final Property property) { + @Override + public SortedSet visit(final Property property) { - final long start = System.nanoTime(); - final String field = property.getField(); - final String value = property.getValue().getValue(); + final long start = System.nanoTime(); + final String field = property.getField(); + final String value = property.getValue().getValue(); - final SortedSet allValuesForField = queryCompletionIndex.findAllValuesForField(dateRange, field); + final SortedSet allValuesForField = queryCompletionIndex.findAllValuesForField(dateRange, field); - final String valuePrefix = value.substring(0, value.indexOf(NewProposerParser.CARET_MARKER)); + final String valuePrefix; - final TreeSet result = GloblikePattern.filterValues(allValuesForField, valuePrefix, TreeSet::new); - METRIC_LOGGER.debug("{}: {}ms", property, (System.nanoTime() - start) / 1_000_000.0); - return result; - } + if (value.indexOf(NewProposerParser.CARET_MARKER) >= 0) { + valuePrefix = value.substring(0, value.indexOf(NewProposerParser.CARET_MARKER)); + } else { + valuePrefix = value; + } + + final TreeSet result = GloblikePattern.filterValues(allValuesForField, valuePrefix, TreeSet::new); + METRIC_LOGGER.debug("{}: {}ms", property, (System.nanoTime() - start) / 1_000_000.0); + return result; + } @Override public SortedSet visit(final AndCaretExpression expression) { diff --git a/data-store/src/test/java/org/lucares/pdb/datastore/internal/ProposerTest.java b/data-store/src/test/java/org/lucares/pdb/datastore/internal/ProposerTest.java index 6bbeebf..7c30d9d 100644 --- a/data-store/src/test/java/org/lucares/pdb/datastore/internal/ProposerTest.java +++ b/data-store/src/test/java/org/lucares/pdb/datastore/internal/ProposerTest.java @@ -73,7 +73,7 @@ public class ProposerTest { public void testEmptyQuery() throws Exception { - assertProposals("", ResultMode.FULL_VALUES, 0, // + assertProposals("|", ResultMode.FULL_VALUES, // new Proposal("name", "name=*", true, "name=", 5), // new Proposal("bird", "bird=*", true, "bird=", 5), // new Proposal("dog", "dog=*", true, "dog=", 4), // @@ -81,7 +81,7 @@ public class ProposerTest { new Proposal("source", "source=*", true, "source=", 7)// ); - assertProposals(" ", ResultMode.FULL_VALUES, 1, // + assertProposals(" |", ResultMode.FULL_VALUES, // new Proposal("name", "name=*", true, "name=", 5), // new Proposal("bird", "bird=*", true, "bird=", 5), // new Proposal("dog", "dog=*", true, "dog=", 4), // @@ -91,48 +91,63 @@ public class ProposerTest { } public void testPrefixOfKey() throws Exception { - assertProposals("bi", ResultMode.FULL_VALUES, 2, // + assertProposals("bi|", ResultMode.FULL_VALUES, // new Proposal("bird", "bird=* ", true, "bird=", 5) // ); - assertProposals("bird", ResultMode.FULL_VALUES, 4, // + assertProposals("bird|", ResultMode.FULL_VALUES, // new Proposal("bird", "bird=* ", true, "bird=", 5) // ); - assertProposals("bird=eagle and n", ResultMode.FULL_VALUES, 16, // + assertProposals("bird=eagle and n|", ResultMode.FULL_VALUES, // new Proposal("name", "bird=eagle and name=* ", true, "bird=eagle and name=", 20) // ); + + assertProposals("|bird", ResultMode.FULL_VALUES, // + new Proposal("bird", "bird=* ", true, "bird=", 5), // + new Proposal("dog", "dog=* ", true, "dog=", 4), // + new Proposal("method", "method=* ", true, "method=", 7), // + new Proposal("name", "name=* ", true, "name=", 5), // + new Proposal("source", "source=* ", true, "source=", 7) // + ); } public void testPrefixOfValue() throws Exception { - assertProposals("name =Tim", ResultMode.FULL_VALUES, 9, // + assertProposals("name =Tim|", ResultMode.FULL_VALUES, // new Proposal("Tim", "name =Tim", true, "name =Tim", 9), new Proposal("Timothy", "name =Timothy", true, "name =Timothy", 13)); - assertProposals("name =Je", ResultMode.FULL_VALUES, 8, // + assertProposals("name =Je|", ResultMode.FULL_VALUES, // new Proposal("Jennifer", "name =Jennifer", true, "name =Jennifer", 14), // new Proposal("Jenny", "name =Jenny", true, "name =Jenny", 11) // ); - assertProposals("name =Tim,Je", ResultMode.FULL_VALUES, 12, // + assertProposals("name =Tim,Je|", ResultMode.FULL_VALUES, // new Proposal("Jennifer", "name =Tim,Jennifer", true, "name =Tim,Jennifer", 18), // new Proposal("Jenny", "name =Tim,Jenny", true, "name =Tim,Jenny", 15) // ); + + // TODO this case is currently handled completely wrong - it is handled similar to an empty query +// assertProposals("|bird=eagle and name=Tim", ResultMode.FULL_VALUES, // +// new Proposal("Jennifer", "name =Tim,Jennifer", true, "name =Tim,Jennifer", 18), // +// new Proposal("Jenny", "name =Tim,Jenny", true, "name =Tim,Jenny", 15) // +// ); + /* */ } @Test(enabled = true) public void testInExpressions() throws Exception { - assertProposals("name = (Timothy,)", ResultMode.FULL_VALUES, 16, // + assertProposals("name = (Timothy,|)", ResultMode.FULL_VALUES, // new Proposal("Jennifer", "name = (Timothy,Jennifer)", true, "name = (Timothy,Jennifer)", 24), // new Proposal("Jenny", "name = (Timothy,Jenny)", true, "name = (Timothy,Jenny)", 21), // new Proposal("Tim", "name = (Timothy,Tim)", true, "name = (Timothy,Tim)", 19), // new Proposal("Timothy", "name = (Timothy,Timothy)", true, "name = (Timothy,Timothy)", 23)// ); - assertProposals("name = (Timothy, J)", ResultMode.FULL_VALUES, 18, // + assertProposals("name = (Timothy, J|)", ResultMode.FULL_VALUES, // new Proposal("Jennifer", "name = (Timothy, Jennifer)", true, "name = (Timothy, Jennifer)", 25), // new Proposal("Jenny", "name = (Timothy, Jenny)", true, "name = (Timothy, Jenny)", 22)); - assertProposals("name = (Tim)", ResultMode.FULL_VALUES, 11, // + assertProposals("name = (Tim|)", ResultMode.FULL_VALUES, // new Proposal("Tim", "name = (Tim)", true, "name = (Tim)", 11), new Proposal("Timothy", "name = (Timothy)", true, "name = (Timothy)", 15)); @@ -141,20 +156,20 @@ public class ProposerTest { } public void testProposalOnEmptyValuePrefix() throws Exception { - assertProposals("name=", ResultMode.FULL_VALUES, 5, // + assertProposals("name=|", ResultMode.FULL_VALUES, // new Proposal("Jennifer", "name=Jennifer", true, "name=Jennifer", 13), // new Proposal("Jenny", "name=Jenny", true, "name=Jenny", 10), // new Proposal("Tim", "name=Tim", true, "name=Tim", 8), // new Proposal("Timothy", "name=Timothy", true, "name=Timothy", 12) // ); - assertProposals("method=", ResultMode.CUT_AT_DOT, 7, // + assertProposals("method=|", ResultMode.CUT_AT_DOT, // new Proposal("FooController.", "method=FooController.", true, "method=FooController.", 21), // new Proposal("FooService.", "method=FooService.", true, "method=FooService.", 18), // new Proposal("BarController.", "method=BarController.", true, "method=BarController.", 21), // new Proposal("FooBarService.", "method=FooBarService.", true, "method=FooBarService.", 21) // ); - assertProposals("method=", ResultMode.FULL_VALUES, 7, // + assertProposals("method=|", ResultMode.FULL_VALUES, // new Proposal("FooController.doImportantStuff", "method=FooController.doImportantStuff", true, "method=FooController.doImportantStuff", 37), // new Proposal("FooService.doImportantStuff", "method=FooService.doImportantStuff", true, @@ -167,7 +182,7 @@ public class ProposerTest { } public void testProposalOnValueSmartExpression() throws Exception { - assertProposals("method=Foo.", ResultMode.CUT_AT_DOT, 11, // + assertProposals("method=Foo.|", ResultMode.CUT_AT_DOT, // new Proposal("FooController.doImportantStuff", "method=FooController.doImportantStuff", true, "method=FooController.doImportantStuff", 37), // new Proposal("FooService.doImportantStuff", "method=FooService.doImportantStuff", true, @@ -176,7 +191,7 @@ public class ProposerTest { "method=FooBarService.doOtherStuff", 33) // ); - assertProposals("method=Foo.*Stuf", ResultMode.CUT_AT_DOT, 16, // + assertProposals("method=Foo.*Stuf|", ResultMode.CUT_AT_DOT, // new Proposal("FooController.doImportantStuff", "method=FooController.doImportantStuff", true, "method=FooController.doImportantStuff", 37), // new Proposal("FooService.doImportantStuff", "method=FooService.doImportantStuff", true, @@ -189,7 +204,7 @@ public class ProposerTest { // following regex: ^[a-z]*Foo.*\.[a-z]*Stuf // Maybe I will change that some day and allow upper case characters before // "Stuff". - assertProposals("method=Foo.Stuf", ResultMode.CUT_AT_DOT, 15); + assertProposals("method=Foo.Stuf|", ResultMode.CUT_AT_DOT); assertProposals("method=Foo.Im", ResultMode.CUT_AT_DOT, 13, // new Proposal("FooController.doImportantStuff", "method=FooController.doImportantStuff", true,