diff options
6 files changed, 306 insertions, 85 deletions
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/TaggableItem.java b/container-search/src/main/java/com/yahoo/prelude/query/TaggableItem.java index 03e85fa3260..4e9d3d11cc5 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/TaggableItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/TaggableItem.java @@ -6,13 +6,13 @@ package com.yahoo.prelude.query; * An interface used for anything which may be addressed using an external, * unique ID in the query tree in the backend. * - * @author Steinar Knutsen + * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a> */ public interface TaggableItem { - int getUniqueID(); - void setUniqueID(int id); - boolean hasUniqueID(); + public int getUniqueID(); + public void setUniqueID(int id); + public boolean hasUniqueID(); /** * Set the connectivity to another term in the same query tree. @@ -30,9 +30,9 @@ public interface TaggableItem { * @param connectivity a value between 0 (none) and 1 (maximal), defining the connectivity between this and the * argument item. The default connectivity is 0.1. */ - void setConnectivity(Item item, double connectivity); - Item getConnectedItem(); - double getConnectivity(); + public void setConnectivity(Item item, double connectivity); + public Item getConnectedItem(); + public double getConnectivity(); /** @@ -41,9 +41,8 @@ public interface TaggableItem { * This influences ranking features which take term significance into account and overrides the default * partial corpus based term significance computation happening in the backend. */ - void setSignificance(double significance); - boolean hasExplicitSignificance(); - void setExplicitSignificance(boolean significance); - double getSignificance(); - + public void setSignificance(double significance); + public boolean hasExplicitSignificance(); + public void setExplicitSignificance(boolean significance); + public double getSignificance(); } diff --git a/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java b/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java index e4ae759eec7..e0e9042e1a3 100644 --- a/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java +++ b/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java @@ -18,8 +18,10 @@ import com.google.common.annotations.Beta; import com.google.common.base.Preconditions; import com.yahoo.collections.LazyMap; import com.yahoo.collections.LazySet; +import com.yahoo.collections.Tuple2; import com.yahoo.component.Version; import com.yahoo.language.Language; +import com.yahoo.language.Linguistics; import com.yahoo.language.detect.Detector; import com.yahoo.language.process.Normalizer; import com.yahoo.language.process.Segmenter; @@ -115,6 +117,9 @@ public class YqlParser implements Parser { private static final String NORMALIZE_CASE_DESCRIPTION = "setting for whether to do case normalization if field implies it"; private static final String ORIGIN_DESCRIPTION = "string origin for a term"; private static final String RANKED_DESCRIPTION = "setting for whether to use term for ranking"; + private static final String SEGMENTER_BACKEND = "backend"; + private static final String SEGMENTER = "segmenter"; + private static final String SEGMENTER_VERSION = "version"; private static final String STEM_DESCRIPTION = "setting for whether to use stem if field implies it"; private static final String USE_POSITION_DATA_DESCRIPTION = "setting for whether to use position data for ranking this item"; private static final String USER_INPUT_ALLOW_EMPTY = "allowEmpty"; @@ -185,6 +190,8 @@ public class YqlParser implements Parser { private final Detector detector; private final Set<String> yqlSources = LazySet.newHashSet(); private final Set<String> yqlSummaryFields = LazySet.newHashSet(); + private final String localSegmenterBackend; + private final Version localSegmenterVersion; private Integer hits; private Integer offset; private Integer timeout; @@ -194,7 +201,10 @@ public class YqlParser implements Parser { private IndexNameExpander indexNameExpander = new IndexNameExpander(); private Set<String> docTypes; private Sorting sorting; + private String segmenterBackend; + private Version segmenterVersion; private boolean queryParser = true; + private boolean resegment = false; private final Deque<OperatorNode<?>> annotationStack = new ArrayDeque<>(); private final ParserEnvironment environment; @@ -228,6 +238,10 @@ public class YqlParser implements Parser { segmenter = environment.getLinguistics().getSegmenter(); detector = environment.getLinguistics().getDetector(); this.environment = environment; + + Tuple2<String, Version> version = environment.getLinguistics().getVersion(Linguistics.Component.SEGMENTER); + localSegmenterBackend = version.first; + localSegmenterVersion = version.second; } @NonNull @@ -247,7 +261,10 @@ public class YqlParser implements Parser { currentlyParsing = query; docTypes = null; sorting = null; + segmenterBackend = null; + segmenterVersion = null; // queryParser set prior to calling this + resegment = false; return buildTree(parseYqlProgram()); } @@ -270,12 +287,32 @@ public class YqlParser implements Parser { filterPart.getArguments().length); populateYqlSources(filterPart.<OperatorNode<?>> getArgument(0)); OperatorNode<ExpressionOperator> filterExpression = filterPart.getArgument(1); + populateLinguisticsAnnotations(filterExpression); Item root = convertExpression(filterExpression); connectItems(); userQuery = null; return new QueryTree(root); } + private void populateLinguisticsAnnotations(OperatorNode<ExpressionOperator> filterExpression) { + Map<?, ?> segmenter = getAnnotation(filterExpression, SEGMENTER, + Map.class, null, "segmenter engine and version"); + if (segmenter == null) { + segmenterVersion = null; + segmenterBackend = null; + resegment = false; + } else { + segmenterBackend = getMapValue(SEGMENTER, segmenter, SEGMENTER_BACKEND, String.class); + try { + segmenterVersion = new Version(getMapValue(SEGMENTER, segmenter, SEGMENTER_VERSION, String.class)); + } catch (RuntimeException e) { + segmenterVersion = null; + } + resegment = ! localSegmenterBackend.equals(segmenterBackend) || + ! localSegmenterVersion.equals(segmenterVersion); + } + } + private void populateYqlSources(OperatorNode<?> filterArgs) { yqlSources.clear(); if (filterArgs.getOperator() == SequenceOperator.SCAN) { @@ -577,7 +614,8 @@ public class YqlParser implements Parser { } phrase.setIndexName(field); - if (getAnnotation(ast, IMPLICIT_TRANSFORMS, Boolean.class, Boolean.TRUE, IMPLICIT_TRANSFORMS_DESCRIPTION)) { + if (resegment + && getAnnotation(ast, IMPLICIT_TRANSFORMS, Boolean.class, Boolean.TRUE, IMPLICIT_TRANSFORMS_DESCRIPTION)) { words = segmenter.segment(origin.getValue(), currentlyParsing.getLanguage()); } @@ -681,16 +719,16 @@ public class YqlParser implements Parser { return Language.ENGLISH; } - private String getStringContents(OperatorNode<ExpressionOperator> operator) { - switch (operator.getOperator()) { + private String getStringContents(OperatorNode<ExpressionOperator> propertySniffer) { + switch (propertySniffer.getOperator()) { case LITERAL: - return operator.getArgument(0, String.class); + return propertySniffer.getArgument(0, String.class); case VARREF: Preconditions.checkState(userQuery != null, "properties must be available when trying to fetch user input"); - return userQuery.properties().getString(operator.getArgument(0, String.class)); + return userQuery.properties().getString(propertySniffer.getArgument(0, String.class)); default: - throw newUnexpectedArgumentException(operator.getOperator(), + throw newUnexpectedArgumentException(propertySniffer.getOperator(), ExpressionOperator.LITERAL, ExpressionOperator.VARREF); } } @@ -1272,20 +1310,22 @@ public class YqlParser implements Parser { wordData = normalizer.normalize(wordData); } boolean fromQuery = getAnnotation(ast, IMPLICIT_TRANSFORMS, - Boolean.class, Boolean.TRUE, IMPLICIT_TRANSFORMS_DESCRIPTION); - boolean prefixMatch = getAnnotation(ast, PREFIX, Boolean.class, Boolean.FALSE, - "setting for whether to use prefix match of input data"); - boolean suffixMatch = getAnnotation(ast, SUFFIX, Boolean.class, Boolean.FALSE, - "setting for whether to use suffix match of input data"); - boolean substrMatch = getAnnotation(ast, SUBSTRING, Boolean.class, Boolean.FALSE, - "setting for whether to use substring match of input data"); - String grammar = getAnnotation(ast, USER_INPUT_GRAMMAR, String.class, - Query.Type.ALL.toString(), "grammar for handling word input"); - Preconditions.checkArgument((prefixMatch ? 1 : 0) + - (substrMatch ? 1 : 0) + (suffixMatch ? 1 : 0) < 2, - "Only one of prefix, substring and suffix can be set."); + Boolean.class, Boolean.TRUE, IMPLICIT_TRANSFORMS_DESCRIPTION); + boolean prefixMatch = getAnnotation(ast, PREFIX, Boolean.class, + Boolean.FALSE, + "setting for whether to use prefix match of input data"); + boolean suffixMatch = getAnnotation(ast, SUFFIX, Boolean.class, + Boolean.FALSE, + "setting for whether to use suffix match of input data"); + boolean substrMatch = getAnnotation(ast, SUBSTRING, Boolean.class, + Boolean.FALSE, + "setting for whether to use substring match of input data"); + Preconditions.checkArgument((prefixMatch ? 1 : 0) + + (substrMatch ? 1 : 0) + (suffixMatch ? 1 : 0) < 2, + "Only one of prefix, substring and suffix can be set."); + @NonNull + final TaggableItem wordItem; - TaggableItem wordItem; if (exactMatch) { wordItem = new ExactStringItem(wordData, fromQuery); } else if (prefixMatch) { @@ -1300,21 +1340,21 @@ public class YqlParser implements Parser { wordItem = new WordItem(wordData, fromQuery); break; case POSSIBLY: - if (shouldSegment(field, fromQuery) && ! grammar.equals(USER_INPUT_RAW)) { - wordItem = segment(field, ast, wordData, fromQuery, parent, language); + if (shouldResegmentWord(field, fromQuery)) { + wordItem = resegment(field, ast, wordData, fromQuery, parent, language); } else { wordItem = new WordItem(wordData, fromQuery); } break; case ALWAYS: - wordItem = segment(field, ast, wordData, fromQuery, parent, language); + wordItem = resegment(field, ast, wordData, fromQuery, parent, language); break; default: throw new IllegalArgumentException("Unexpected segmenting rule: " + segmentPolicy); } } if (wordItem instanceof WordItem) { - prepareWord(field, ast, (WordItem) wordItem); + prepareWord(field, ast, fromQuery, (WordItem) wordItem); } if (language != Language.ENGLISH) // mark the language used, unless it's the default ((Item)wordItem).setLanguage(language); @@ -1322,13 +1362,13 @@ public class YqlParser implements Parser { } @SuppressWarnings({"deprecation"}) - private boolean shouldSegment(String field, boolean fromQuery) { - return fromQuery && ! indexFactsSession.getIndex(field).isAttribute(); + private boolean shouldResegmentWord(String field, boolean fromQuery) { + return resegment && fromQuery && ! indexFactsSession.getIndex(field).isAttribute(); } @NonNull - private TaggableItem segment(String field, OperatorNode<ExpressionOperator> ast, String wordData, - boolean fromQuery, Class<?> parent, Language language) { + private TaggableItem resegment(String field, OperatorNode<ExpressionOperator> ast, String wordData, + boolean fromQuery, Class<?> parent, Language language) { String toSegment = wordData; Substring s = getOrigin(ast); Language usedLanguage = language == null ? currentlyParsing.getLanguage() : language; @@ -1347,7 +1387,7 @@ public class YqlParser implements Parser { ((PhraseSegmentItem) wordItem).setIndexName(field); for (String w : words) { WordItem segment = new WordItem(w, fromQuery); - prepareWord(field, ast, segment); + prepareWord(field, ast, fromQuery, segment); ((PhraseSegmentItem) wordItem).addItem(segment); } ((PhraseSegmentItem) wordItem).lock(); @@ -1364,9 +1404,16 @@ public class YqlParser implements Parser { return parent == EquivItem.class; } - private void prepareWord(String field, OperatorNode<ExpressionOperator> ast, WordItem wordItem) { + private void prepareWord(String field, OperatorNode<ExpressionOperator> ast, boolean fromQuery, + WordItem wordItem) { wordItem.setIndexName(field); wordStyleSettings(ast, wordItem); + if (shouldResegmentWord(field, fromQuery)) { + // force re-stemming, new case normalization, etc + wordItem.setStemmed(false); + wordItem.setLowercased(false); + wordItem.setNormalizable(true); + } } @NonNull @@ -1374,12 +1421,10 @@ public class YqlParser implements Parser { { Map<?, ?> connectivity = getAnnotation(ast, CONNECTIVITY, Map.class, null, "connectivity settings"); if (connectivity != null) { - connectedItems.add(new ConnectedItem(out, - getMapValue(CONNECTIVITY, connectivity, CONNECTION_ID, - Integer.class), getMapValue(CONNECTIVITY, - connectivity, - CONNECTION_WEIGHT, - Number.class).doubleValue())); + connectedItems.add(new ConnectedItem(out, getMapValue( + CONNECTIVITY, connectivity, CONNECTION_ID, + Integer.class), getMapValue(CONNECTIVITY, connectivity, + CONNECTION_WEIGHT, Number.class).doubleValue())); } Number significance = getAnnotation(ast, SIGNIFICANCE, Number.class, null, "term significance"); if (significance != null) { @@ -1668,6 +1713,14 @@ public class YqlParser implements Parser { return new IllegalArgumentException(out.toString()); } + String getSegmenterBackend() { + return segmenterBackend; + } + + Version getSegmenterVersion() { + return segmenterVersion; + } + private static final class ConnectedItem { final double weight; diff --git a/container-search/src/test/java/com/yahoo/search/yql/ResegmentingTestCase.java b/container-search/src/test/java/com/yahoo/search/yql/ResegmentingTestCase.java new file mode 100644 index 00000000000..1bed8ff2233 --- /dev/null +++ b/container-search/src/test/java/com/yahoo/search/yql/ResegmentingTestCase.java @@ -0,0 +1,147 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.yql; + +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.yahoo.search.query.parser.Parsable; +import com.yahoo.search.query.parser.ParserEnvironment; + +/** + * Check rules for resegmenting words in YQL+ when segmenter is deemed + * incompatible. The class under testing is {@link YqlParser}. + * + * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a> + */ +public class ResegmentingTestCase { + private YqlParser parser; + + @Before + public void setUp() throws Exception { + ParserEnvironment env = new ParserEnvironment(); + parser = new YqlParser(env); + } + + @After + public void tearDown() throws Exception { + parser = null; + } + + @Test + public final void testWord() { + assertEquals( + "title:'a b'", + parser.parse( + new Parsable() + .setQuery("select * from sources * where [{\"segmenter\": {\"version\": \"18.47.39\", \"backend\": \"nonexistant\"}}] (title contains \"a b\");")) + .toString()); + } + + @Test + public final void testPhraseSegment() { + assertEquals( + "title:'c d'", + parser.parse( + new Parsable() + .setQuery("select * from sources * where" + + " [{\"segmenter\": {\"version\": \"18.47.39\", \"backend\": \"nonexistant\"}}]" + + " (title contains ([{\"origin\": {\"offset\": 0, \"length\":3, \"original\": \"c d\"}}]" + + " phrase(\"a\", \"b\")));")) + .toString()); + } + + @Test + public final void testPhraseInEquiv() { + assertEquals( + "EQUIV title:a title:'c d'", + parser.parse( + new Parsable() + .setQuery("select * from sources * where" + + " [{\"segmenter\": {\"version\": \"18.47.39\", \"backend\": \"nonexistant\"}}]" + + " (title contains" + + " equiv(\"a\"," + + " ([{\"origin\": {\"offset\": 0, \"length\":3, \"original\": \"c d\"}}]\"b\")" + + ")" + + ");")) + .toString()); + } + + @Test + public final void testPhraseSegmentToAndSegment() { + assertEquals( + "SAND title:c title:d", + parser.parse( + new Parsable() + .setQuery("select * from sources * where" + + " [{\"segmenter\": {\"version\": \"18.47.39\", \"backend\": \"nonexistant\"}}]" + + " (title contains ([{\"origin\": {\"offset\": 0, \"length\":3, \"original\": \"c d\"}, \"andSegmenting\": true}]" + + " phrase(\"a\", \"b\")));")) + .toString()); + } + + @Test + public final void testPhraseSegmentInPhrase() { + assertEquals( + "title:\"a 'c d'\"", + parser.parse( + new Parsable() + .setQuery("select * from sources * where [{\"segmenter\": {\"version\": \"18.47.39\", \"backend\": \"nonexistant\"}}]" + + " (title contains phrase(\"a\"," + + " ([{\"origin\": {\"offset\": 0, \"length\":3, \"original\": \"c d\"}}]" + + " phrase(\"e\", \"f\"))));")) + .toString()); + } + + @Test + public final void testWordNoImplicitTransforms() { + assertEquals( + "title:a b", + parser.parse( + new Parsable() + .setQuery("select * from sources * where [{\"segmenter\": {\"version\": \"18.47.39\", \"backend\": \"nonexistant\"}}] (title contains ([{\"implicitTransforms\": false}]\"a b\"));")) + .toString()); + } + + @Test + public final void testPhraseSegmentNoImplicitTransforms() { + assertEquals( + "title:'a b'", + parser.parse( + new Parsable() + .setQuery("select * from sources * where" + + " [{\"segmenter\": {\"version\": \"18.47.39\", \"backend\": \"nonexistant\"}}]" + + " (title contains ([{\"origin\": {\"offset\": 0, \"length\":3, \"original\": \"c d\"}, \"implicitTransforms\": false}]" + + " phrase(\"a\", \"b\")));")) + .toString()); + } + + @Test + public final void testPhraseSegmentToAndSegmentNoImplicitTransforms() { + assertEquals( + "SAND title:a title:b", + parser.parse( + new Parsable() + .setQuery("select * from sources * where" + + " [{\"segmenter\": {\"version\": \"18.47.39\", \"backend\": \"nonexistant\"}}]" + + " (title contains ([{\"origin\": {\"offset\": 0, \"length\":3, \"original\": \"c d\"}, \"andSegmenting\": true, \"implicitTransforms\": false}]" + + " phrase(\"a\", \"b\")));")) + .toString()); + } + + @Test + public final void testPhraseSegmentInPhraseNoImplicitTransforms() { + assertEquals( + "title:\"a 'e f'\"", + parser.parse( + new Parsable() + .setQuery("select * from sources * where [{\"segmenter\": {\"version\": \"18.47.39\", \"backend\": \"nonexistant\"}}]" + + " (title contains phrase(\"a\"," + + " ([{\"origin\": {\"offset\": 0, \"length\":3, \"original\": \"c d\"}, \"implicitTransforms\": false}]" + + " phrase(\"e\", \"f\"))));")) + .toString()); + } + +} diff --git a/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java b/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java index 127820bb7ae..e2325e52f63 100644 --- a/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java @@ -11,7 +11,6 @@ import com.yahoo.prelude.query.IndexedItem; import com.yahoo.prelude.query.ExactStringItem; import com.yahoo.prelude.query.Item; import com.yahoo.prelude.query.PhraseItem; -import com.yahoo.prelude.query.PhraseSegmentItem; import com.yahoo.prelude.query.PrefixItem; import com.yahoo.prelude.query.QueryCanonicalizer; import com.yahoo.prelude.query.RegExpItem; @@ -56,15 +55,15 @@ import static org.junit.Assert.fail; /** * Specification for the conversion of YQL+ expressions to Vespa search queries. * - * @author Steinar Knutsen - * @author Stian Kristoffersen + * @author steinar + * @author stiankri */ public class YqlParserTestCase { private final YqlParser parser = new YqlParser(new ParserEnvironment()); @Test - public void testParserDefaults() { + public void requireThatDefaultsAreSane() { assertTrue(parser.isQueryParser()); assertNull(parser.getDocTypes()); } @@ -77,7 +76,7 @@ public class YqlParserTestCase { } @Test - public void testGroupingStep() { + public void requireThatGroupingStepCanBeParsed() { assertParse("select foo from bar where baz contains 'cox';", "baz:cox"); assertEquals("[]", @@ -99,7 +98,7 @@ public class YqlParserTestCase { } @Test - public void testGroupingContinuation() { + public void requireThatGroupingContinuationCanBeParsed() { assertParse("select foo from bar where baz contains 'cox' " + "| [{ 'continuations': ['BCBCBCBEBG', 'BCBKCBACBKCCK'] }]all(group(a) each(output(count())));", "baz:cox"); @@ -321,14 +320,12 @@ public class YqlParserTestCase { @Test public void testRaw() { - // Default: Not raw, for comparison Item root = parse("select foo from bar where baz contains (\"yoni jo dima\");").getRoot(); - assertEquals("baz:'yoni jo dima'", root.toString()); - assertFalse(root instanceof WordItem); - assertTrue(root instanceof PhraseSegmentItem); + assertTrue(root instanceof WordItem); + assertFalse(root instanceof ExactStringItem); + assertEquals("yoni jo dima", ((WordItem)root).getWord()); root = parse("select foo from bar where baz contains ([{\"grammar\":\"raw\"}]\"yoni jo dima\");").getRoot(); - assertEquals("baz:yoni jo dima", root.toString()); assertTrue(root instanceof WordItem); assertFalse(root instanceof ExactStringItem); assertEquals("yoni jo dima", ((WordItem)root).getWord()); @@ -738,17 +735,44 @@ public class YqlParserTestCase { @Test public void testSegmenting() { - assertParse("select * from bar where title contains 'foo.bar';", - "title:'foo bar'"); - - assertParse("select * from bar where title contains 'foo&123';", - "title:'foo 123'"); + assertParse("select * from bar where ([{\"segmenter\": {\"version\": \"58.67.49\", \"backend\": " + + "\"yell\"}}] title contains \"madonna\");", + "title:madonna"); + assertEquals("yell", parser.getSegmenterBackend()); + assertEquals(new Version("58.67.49"), parser.getSegmenterVersion()); + + assertParse("select * from bar where ([{\"segmenter\": {\"version\": \"8.7.3\", \"backend\": " + + "\"yell\"}}]([{\"targetNumHits\": 9999438}] weakAnd(format contains \"online\", title contains " + + "\"madonna\")));", + "WAND(9999438) format:online title:madonna"); + assertEquals("yell", parser.getSegmenterBackend()); + assertEquals(new Version("8.7.3"), parser.getSegmenterVersion()); + + assertParse("select * from bar where [{\"segmenter\": {\"version\": \"18.47.39\", \"backend\": " + + "\"yell\"}}] ([{\"targetNumHits\": 99909438}] weakAnd(format contains \"online\", title contains " + + "\"madonna\"));", + "WAND(99909438) format:online title:madonna"); + assertEquals("yell", parser.getSegmenterBackend()); + assertEquals(new Version("18.47.39"), parser.getSegmenterVersion()); + + assertParse("select * from bar where [{\"targetNumHits\": 99909438}] weakAnd(format contains " + + "\"online\", title contains \"madonna\");", + "WAND(99909438) format:online title:madonna"); + assertNull(parser.getSegmenterBackend()); + assertNull(parser.getSegmenterVersion()); + + assertParse("select * from bar where [{\"segmenter\": {\"version\": \"58.67.49\", \"backend\": " + + "\"yell\"}}](title contains \"madonna\") order by shoesize;", + "title:madonna"); + assertEquals("yell", parser.getSegmenterBackend()); + assertEquals(new Version("58.67.49"), parser.getSegmenterVersion()); } @Test public void testNegativeHitLimit() { - assertParse("select * from sources * where [{\"hitLimit\": -38}]range(foo, 0, 1);", - "foo:[0;1;-38]"); + assertParse( + "select * from sources * where [{\"hitLimit\": -38}]range(foo, 0, 1);", + "foo:[0;1;-38]"); } @Test @@ -806,26 +830,26 @@ public class YqlParserTestCase { @Test public void testMoreInheritedAnnotations() { - String yqlQuery = "select * from sources * where " + - "([{\"ranked\": false}](foo contains \"a\" " + - "and ([{\"ranked\": true}](bar contains \"b\" " + - "or ([{\"ranked\": false}](foo contains \"c\" " + - "and foo contains ([{\"ranked\": true}]\"d\")))))));"; + final String yqlQuery = "select * from sources * where " + + "([{\"ranked\": false}](foo contains \"a\" " + + "and ([{\"ranked\": true}](bar contains \"b\" " + + "or ([{\"ranked\": false}](foo contains \"c\" " + + "and foo contains ([{\"ranked\": true}]\"d\")))))));"; QueryTree x = parse(yqlQuery); List<IndexedItem> terms = QueryTree.getPositiveTerms(x); assertEquals(4, terms.size()); for (IndexedItem term : terms) { switch (term.getIndexedString()) { - case "a": - case "c": - assertFalse(((Item) term).isRanked()); - break; - case "b": - case "d": - assertTrue(((Item) term).isRanked()); - break; - default: - fail(); + case "a": + case "c": + assertFalse(((Item) term).isRanked()); + break; + case "b": + case "d": + assertTrue(((Item) term).isRanked()); + break; + default: + fail(); } } } @@ -897,8 +921,8 @@ public class YqlParserTestCase { private void checkWordAlternativesContent(WordAlternativesItem alternatives) { boolean seenTree = false; boolean seenForest = false; - String forest = "trees"; - String tree = "tree"; + final String forest = "trees"; + final String tree = "tree"; assertEquals(2, alternatives.getAlternatives().size()); for (WordAlternativesItem.Alternative alternative : alternatives.getAlternatives()) { if (tree.equals(alternative.word)) { diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/Timer.java b/jdisc_core/src/main/java/com/yahoo/jdisc/Timer.java index a111425fbc0..a822ed46ef9 100644 --- a/jdisc_core/src/main/java/com/yahoo/jdisc/Timer.java +++ b/jdisc_core/src/main/java/com/yahoo/jdisc/Timer.java @@ -35,5 +35,4 @@ public interface Timer { default Instant currentTime() { return Instant.ofEpochMilli(currentTimeMillis()); } - } diff --git a/vespajlib/src/main/java/com/yahoo/concurrent/Timer.java b/vespajlib/src/main/java/com/yahoo/concurrent/Timer.java index 9097da53bd4..af8054c735d 100644 --- a/vespajlib/src/main/java/com/yahoo/concurrent/Timer.java +++ b/vespajlib/src/main/java/com/yahoo/concurrent/Timer.java @@ -15,6 +15,5 @@ public interface Timer { * * @return The current value of the timer, in milliseconds. */ - long milliTime(); - + public long milliTime(); } |