summaryrefslogtreecommitdiffstats
path: root/container-search
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@oath.com>2021-02-09 22:31:29 +0100
committerGitHub <noreply@github.com>2021-02-09 22:31:29 +0100
commit4dad2f21a8273d6984190f9311380a96cc8f37c6 (patch)
tree3bf683ce6c57ded84c27cae3eda4d5937967835c /container-search
parent54f98e3e386a7b78d2f1760bab3de9b58341345e (diff)
parent2e0c5d67cece9811042cf6195394cf785bd0694b (diff)
Merge pull request #16411 from karowan/karowan/replacing-or-by-wand-16360
Karowan/replacing or by wand 16360
Diffstat (limited to 'container-search')
-rw-r--r--container-search/src/main/java/com/yahoo/search/querytransform/WeakAndReplacementSearcher.java66
-rw-r--r--container-search/src/test/java/com/yahoo/search/querytransform/WeakAndReplacementSearcherTestCase.java119
2 files changed, 185 insertions, 0 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/querytransform/WeakAndReplacementSearcher.java b/container-search/src/main/java/com/yahoo/search/querytransform/WeakAndReplacementSearcher.java
new file mode 100644
index 00000000000..058f6b93ae3
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/querytransform/WeakAndReplacementSearcher.java
@@ -0,0 +1,66 @@
+package com.yahoo.search.querytransform;
+
+import com.yahoo.prelude.query.*;
+import com.yahoo.processing.request.CompoundName;
+import com.yahoo.search.Query;
+import com.yahoo.search.Result;
+import com.yahoo.search.Searcher;
+import com.yahoo.search.searchchain.Execution;
+
+/**
+ * Recursively replaces all instances of OrItems with WeakAndItems if the query property weakand.replace is true.
+ * Otherwise a noop searcher.
+ *
+ * @author karowan
+ */
+public class WeakAndReplacementSearcher extends Searcher {
+ private static final CompoundName WEAKAND_REPLACE = new CompoundName("weakAnd.replace");
+
+ @Override public Result search(Query query, Execution execution) {
+ if (!query.properties().getBoolean(WEAKAND_REPLACE)) {
+ return execution.search(query);
+ }
+ replaceOrItems(query);
+ return execution.search(query);
+ }
+
+ /**
+ * Extracts the queryTree root and the wand.hits property to send to the recursive replacement function
+ * @param query the search query
+ */
+ private void replaceOrItems(Query query) {
+ Item root = query.getModel().getQueryTree().getRoot();
+ int hits = query.properties().getInteger("wand.hits", WeakAndItem.defaultN);
+ query.getModel().getQueryTree().setRoot(replaceOrItems(root, hits));
+ if (root != query.getModel().getQueryTree().getRoot())
+ query.trace("Replaced OR by WeakAnd", true, 2);
+ }
+
+
+ /**
+ * Recursively iterates over an Item to replace all instances of OrItems with WeakAndItems
+ * @param item the current item in the replacement iteration
+ * @param hits the wand.hits property from the request which is assigned to the N value of the new WeakAndItem
+ * @return The original item or a WeakAndItem replacement of an OrItem
+ */
+ private Item replaceOrItems(Item item, int hits) {
+ if (!(item instanceof CompositeItem)) {
+ return item;
+ }
+ CompositeItem compositeItem = (CompositeItem) item;
+ if (compositeItem instanceof OrItem) {
+ WeakAndItem newItem = new WeakAndItem(hits);
+ newItem.setWeight(compositeItem.getWeight());
+ compositeItem.items().forEach(newItem::addItem);
+ compositeItem = newItem;
+ }
+ for (int i = 0; i < compositeItem.getItemCount(); i++) {
+ Item subItem = compositeItem.getItem(i);
+ Item replacedItem = replaceOrItems(subItem, hits);
+ if (replacedItem != subItem) {
+ compositeItem.setItem(i, replacedItem);
+ }
+ }
+ return compositeItem;
+ }
+}
diff --git a/container-search/src/test/java/com/yahoo/search/querytransform/WeakAndReplacementSearcherTestCase.java b/container-search/src/test/java/com/yahoo/search/querytransform/WeakAndReplacementSearcherTestCase.java
new file mode 100644
index 00000000000..1aa61d7535b
--- /dev/null
+++ b/container-search/src/test/java/com/yahoo/search/querytransform/WeakAndReplacementSearcherTestCase.java
@@ -0,0 +1,119 @@
+package com.yahoo.search.querytransform;
+
+import com.yahoo.component.chain.Chain;
+import com.yahoo.prelude.query.*;
+import com.yahoo.processing.request.CompoundName;
+import com.yahoo.search.Query;
+import com.yahoo.search.Result;
+import com.yahoo.search.Searcher;
+import com.yahoo.search.searchchain.Execution;
+import org.junit.Test;
+
+import java.util.stream.IntStream;
+
+import static org.junit.Assert.*;
+
+public class WeakAndReplacementSearcherTestCase {
+
+ private static final CompoundName WEAKAND_REPLACE = new CompoundName("weakAnd.replace");
+ private static final int N = 99;
+
+
+ private Execution buildExec() {
+ return new Execution(new Chain<Searcher>(new WeakAndReplacementSearcher()),
+ Execution.Context.createContextStub());
+ }
+
+ private Query buildDefaultQuery(boolean searcherEnabled) {
+ Query query = new Query();
+ query.properties().set("wand.hits", N);
+ query.properties().set(WEAKAND_REPLACE, searcherEnabled);
+ OrItem root = new OrItem();
+ root.addItem(new WordItem("text"));
+ NotItem notItem = new NotItem();
+ OrItem notItemOr = new OrItem();
+ notItemOr.addItem(new IntItem(1, "index"));
+ notItemOr.addItem(new WordItem("positive"));
+ notItem.addPositiveItem(notItemOr);
+ notItem.addNegativeItem(new WordItem("negative"));
+ query.getModel().getQueryTree().setRoot(root);
+ return query;
+ }
+
+
+
+
+ @Test
+ public void requireOrItemsToBeReplaced() {
+ Query query = buildDefaultQuery(true);
+ Result result = buildExec().search(query);
+ Item root = TestUtils.getQueryTreeRoot(result);
+ assertFalse(orItemsExist(root));
+ assertTrue(root instanceof WeakAndItem);
+ assertEquals(N, ((WeakAndItem)root).getN());
+ }
+
+ @Test
+ public void requireQueryPropertyToWork() {
+ Query query = buildDefaultQuery(false);
+ Item preRoot = query.getModel().getQueryTree().getRoot();
+ Result result = buildExec().search(query);
+ Item root = TestUtils.getQueryTreeRoot(result);
+ assertTrue(orItemsExist(root));
+ assertTrue(deepEquals(root, preRoot));
+ }
+
+ @Test
+ public void requireDoNothingOnNoOrItems() {
+ Query query = new Query();
+ query.properties().set(WEAKAND_REPLACE, true);
+ AndItem andItem = new AndItem();
+ andItem.addItem(new WordItem("1"));
+ andItem.addItem(new WordItem("2"));
+ query.getModel().getQueryTree().setRoot(andItem);
+ Result result = buildExec().search(query);
+ Item root = TestUtils.getQueryTreeRoot(result);
+ assertTrue(deepEquals(root, andItem));
+ }
+
+ @Test
+ public void requireChildrenAreTheSame() {
+ Query query = new Query();
+ query.properties().set(WEAKAND_REPLACE, true);
+ OrItem preRoot = new OrItem();
+ preRoot.addItem(new WordItem("val1"));
+ preRoot.addItem(new WordItem("val2"));
+
+ query.getModel().getQueryTree().setRoot(preRoot);
+ Result result = buildExec().search(query);
+ WeakAndItem root = (WeakAndItem)TestUtils.getQueryTreeRoot(result);
+ assertEquals(preRoot.getItem(0), root.getItem(0));
+ assertEquals(preRoot.getItem(1), root.getItem(1));
+ }
+
+ private boolean deepEquals(Item item1, Item item2) {
+ if (item1 != item2) {
+ return false;
+ }
+ if (!(item1 instanceof CompositeItem)) {
+ return true;
+ }
+
+ CompositeItem compositeItem1 = (CompositeItem) item1;
+ CompositeItem compositeItem2 = (CompositeItem) item2;
+ return IntStream.range(0, compositeItem1.getItemCount())
+ .allMatch(i -> deepEquals(compositeItem1.getItem(i), compositeItem2.getItem(i)));
+ }
+
+ private boolean orItemsExist(Item item) {
+ if (!(item instanceof CompositeItem)) {
+ return false;
+ }
+ if (item instanceof OrItem) {
+ return true;
+ }
+ CompositeItem compositeItem = (CompositeItem) item;
+ return compositeItem.items().stream().anyMatch(this::orItemsExist);
+ }
+
+}