diff options
Diffstat (limited to 'container-search/src/main/java/com/yahoo/prelude/query/parser/AnyParser.java')
-rw-r--r-- | container-search/src/main/java/com/yahoo/prelude/query/parser/AnyParser.java | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/parser/AnyParser.java b/container-search/src/main/java/com/yahoo/prelude/query/parser/AnyParser.java new file mode 100644 index 00000000000..3043cb27247 --- /dev/null +++ b/container-search/src/main/java/com/yahoo/prelude/query/parser/AnyParser.java @@ -0,0 +1,266 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.prelude.query.parser; + +import com.yahoo.language.Language; +import com.yahoo.prelude.IndexFacts; +import com.yahoo.prelude.query.*; +import com.yahoo.search.query.parser.ParserEnvironment; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; + +import static com.yahoo.prelude.query.parser.Token.Kind.*; + +/** + * Parser for queries of type any. + * + * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a> + */ +public class AnyParser extends SimpleParser { + + public AnyParser(ParserEnvironment environment) { + super(environment); + } + + protected Item parseItems() { + return anyItems(true); + } + + Item parseFilter(String filter, Language queryLanguage, Set<String> searchDefinitions) { + return parseFilter(filter, queryLanguage, environment.getIndexFacts().newSession(searchDefinitions, Collections.emptySet())); + } + + Item parseFilter(String filter, Language queryLanguage, IndexFacts.Session indexFacts) { + Item filterRoot; + + setState(queryLanguage, indexFacts); + tokenize(filter, null, indexFacts); + + filterRoot = anyItems(true); + + if (filterRoot == null) { + return null; + } + + markAllTermsAsFilters(filterRoot); + return filterRoot; + } + + protected Item negativeItem() { + int position = tokens.getPosition(); + Item item = null; + + try { + tokens.skipMultiple(PLUS); + + if (!tokens.skipMultiple(MINUS)) { + return null; + } + + if (tokens.currentIsNoIgnore(SPACE)) { + return null; + } + + if (item == null) { + item = indexableItem(); + } + + if (item == null) { + item = compositeItem(); + + if (item != null) { + if (item instanceof OrItem) { // Turn into And + AndItem and = new AndItem(); + + for (Iterator<Item> i = ((OrItem) item).getItemIterator(); i.hasNext();) { + and.addItem(i.next()); + } + item = and; + } + } + } + if (item!=null) + item.setProtected(true); + return item; + } finally { + if (item == null) { + tokens.setPosition(position); + } + } + } + + /** + * Returns the top level item resulting from combining the given top + * level item and the new item. This implements most of the weird transformation + * rules of the parser. + */ + protected Item combineItems(Item topLevelItem, Item item) { + if (topLevelItem == null) { + return item; + } else if (topLevelItem instanceof OrItem && item instanceof OrItem) { + OrItem newTopOr = new OrItem(); + + newTopOr.addItem(topLevelItem); + newTopOr.addItem(item); + return newTopOr; + } else if (!(topLevelItem instanceof RankItem)) { + RankItem rank = new RankItem(); + + if (topLevelItem instanceof NotItem) { // Strange rule, but that's how it is + rank.addItem(topLevelItem); + rank.addItem(item); + } else { + rank.addItem(item); + rank.addItem(topLevelItem); + } + return rank; + } else if ((topLevelItem instanceof RankItem) + && (item instanceof RankItem) + && (((RankItem) item).getItem(0) instanceof OrItem)) { + RankItem itemAsRank = (RankItem) item; + OrItem or = (OrItem) itemAsRank.getItem(0); + + ((RankItem) topLevelItem).addItem(0, or); + for (int i = 1; i < itemAsRank.getItemCount(); i++) { + or.addItem(0, itemAsRank.getItem(i)); + } + return topLevelItem; + } else { + ((RankItem) topLevelItem).addItem(0, item); + return topLevelItem; + } + } + + Item applyFilter(Item root, String filter, Language queryLanguage, IndexFacts.Session indexFacts) { + setState(queryLanguage, indexFacts); + tokenize(filter, null, indexFacts); + return filterItems(root); + } + + private void markAllTermsAsFilters(Item root) { + if (root instanceof BlockItem) { + root.setFilter(true); + } + + if (root instanceof TermItem) { + root.setFilter(true); + } else { + if (root instanceof PhraseItem) { + root.setFilter(true); + } + for (Iterator<Item> i = ((CompositeItem) root).getItemIterator(); i.hasNext();) { + markAllTermsAsFilters(i.next()); + } + } + } + + private Item filterItems(Item root) { + while (tokens.hasNext()) { + Item item = null; + + item = positiveItem(); + root = addAndFilter(root, item); + if (item == null) { + item = negativeItem(); + root = addNotFilter(root, item); + } + if (item == null) { + item = indexableItem(); + root = addRankFilter(root, item); + } + + if (item != null) { + markAllTermsAsFilters(item); + } else { + tokens.skip(); + } + } + return root; + } + + private Item addAndFilter(Item root, Item item) { + if (item == null) { + return root; + } + + if (root instanceof AndItem) { + ((AndItem) root).addItem(item); + return root; + } + + if (root instanceof RankItem) { + Item firstChild = ((RankItem) root).getItem(0); + + if (firstChild instanceof AndItem) { + ((AndItem) firstChild).addItem(item); + return root; + } else if (firstChild instanceof NotItem) { + ((NotItem) firstChild).addPositiveItem(item); + return root; + } + } + + AndItem and = new AndItem(); + + and.addItem(root); + and.addItem(item); + return and; + } + + private Item addNotFilter(Item root, Item item) { + if (item == null) { + return root; + } + + if (root instanceof NotItem) { + ((NotItem) root).addNegativeItem(item); + return root; + } + + if (root instanceof RankItem) { + RankItem rootAsRank = (RankItem) root; + Item firstChild = rootAsRank.getItem(0); + + if (firstChild instanceof NotItem) { + ((NotItem) firstChild).addNegativeItem(item); + return root; + } else { + NotItem not = new NotItem(); + + not.addPositiveItem(rootAsRank.removeItem(0)); + not.addNegativeItem(item); + if (rootAsRank.getItemCount() == 0) { + return not; + } else { + rootAsRank.addItem(0, not); + return root; + } + } + } + + NotItem not = new NotItem(); + + not.addPositiveItem(root); + not.addNegativeItem(item); + return not; + } + + private Item addRankFilter(Item root, Item item) { + if (item == null) { + return root; + } + + if (root instanceof RankItem) { + ((RankItem) root).addItem(item); + return root; + } + + RankItem rank = new RankItem(); + + rank.addItem(root); + rank.addItem(item); + return rank; + } + +} |