From 5edcd157eb99da504d96abd8687b24fdf448fabb Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Wed, 27 Oct 2021 12:39:40 +0200 Subject: Always compare item content fully in Item.equals --- .../main/java/com/yahoo/prelude/query/AndItem.java | 4 + .../com/yahoo/prelude/query/AndSegmentItem.java | 2 - .../java/com/yahoo/prelude/query/BlockItem.java | 11 +- .../yahoo/prelude/query/CompositeIndexedItem.java | 13 +-- .../com/yahoo/prelude/query/CompositeItem.java | 77 ++++++------- .../yahoo/prelude/query/CompositeTaggableItem.java | 6 +- .../com/yahoo/prelude/query/ExactStringItem.java | 4 + .../com/yahoo/prelude/query/GeoLocationItem.java | 8 +- .../java/com/yahoo/prelude/query/HasIndexItem.java | 2 +- .../java/com/yahoo/prelude/query/Highlight.java | 22 ++-- .../yahoo/prelude/query/IndexedSegmentItem.java | 7 +- .../main/java/com/yahoo/prelude/query/IntItem.java | 3 +- .../main/java/com/yahoo/prelude/query/Item.java | 65 +++++------ .../java/com/yahoo/prelude/query/ItemHelper.java | 32 ------ .../com/yahoo/prelude/query/MarkerWordItem.java | 13 +-- .../java/com/yahoo/prelude/query/NearItem.java | 15 +-- .../yahoo/prelude/query/NearestNeighborItem.java | 22 +++- .../main/java/com/yahoo/prelude/query/NotItem.java | 20 ++-- .../java/com/yahoo/prelude/query/NullItem.java | 20 ++-- .../java/com/yahoo/prelude/query/ONearItem.java | 1 - .../main/java/com/yahoo/prelude/query/OrItem.java | 2 + .../java/com/yahoo/prelude/query/PhraseItem.java | 4 + .../com/yahoo/prelude/query/PhraseSegmentItem.java | 10 +- .../yahoo/prelude/query/PredicateQueryItem.java | 122 +++++++++++++++------ .../java/com/yahoo/prelude/query/PrefixItem.java | 3 + .../yahoo/prelude/query/PureWeightedInteger.java | 18 ++- .../com/yahoo/prelude/query/PureWeightedItem.java | 12 +- .../yahoo/prelude/query/PureWeightedString.java | 18 ++- .../yahoo/prelude/query/QueryCanonicalizer.java | 2 +- .../java/com/yahoo/prelude/query/RangeItem.java | 1 - .../java/com/yahoo/prelude/query/RankItem.java | 2 + .../com/yahoo/prelude/query/SameElementItem.java | 14 +++ .../java/com/yahoo/prelude/query/SegmentItem.java | 65 +++++++---- .../com/yahoo/prelude/query/SimpleIndexedItem.java | 3 +- .../java/com/yahoo/prelude/query/Substring.java | 20 +++- .../com/yahoo/prelude/query/SubstringItem.java | 4 +- .../java/com/yahoo/prelude/query/SuffixItem.java | 4 +- .../java/com/yahoo/prelude/query/TaggableItem.java | 1 - .../yahoo/prelude/query/TaggableSegmentItem.java | 11 ++ .../java/com/yahoo/prelude/query/TermItem.java | 26 ++++- .../java/com/yahoo/prelude/query/TermType.java | 14 +-- .../main/java/com/yahoo/prelude/query/UriItem.java | 16 +++ .../java/com/yahoo/prelude/query/WandItem.java | 16 +++ .../java/com/yahoo/prelude/query/WeakAndItem.java | 9 +- .../com/yahoo/prelude/query/WeightedSetItem.java | 29 +++-- .../yahoo/prelude/query/WordAlternativesItem.java | 65 +++++++---- .../java/com/yahoo/prelude/query/WordItem.java | 21 ++-- 47 files changed, 544 insertions(+), 315 deletions(-) (limited to 'container-search/src/main/java/com/yahoo') diff --git a/container-search/src/main/java/com/yahoo/prelude/query/AndItem.java b/container-search/src/main/java/com/yahoo/prelude/query/AndItem.java index d1a05556093..d5fbb193cfc 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/AndItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/AndItem.java @@ -17,4 +17,8 @@ public class AndItem extends CompositeItem { return "AND"; } + public boolean equals(Object p) { + return super.equals(p); + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/AndSegmentItem.java b/container-search/src/main/java/com/yahoo/prelude/query/AndSegmentItem.java index 2c73ffa772b..ba8e40bd5e0 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/AndSegmentItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/AndSegmentItem.java @@ -50,8 +50,6 @@ public class AndSegmentItem extends SegmentItem implements BlockItem { } } - // TODO: Is it necessary to override equals? - public void setWeight(int w) { for (Iterator i = getItemIterator(); i.hasNext();) { i.next().setWeight(w); diff --git a/container-search/src/main/java/com/yahoo/prelude/query/BlockItem.java b/container-search/src/main/java/com/yahoo/prelude/query/BlockItem.java index c2fd207f548..c3962c97356 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/BlockItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/BlockItem.java @@ -16,21 +16,20 @@ public interface BlockItem extends HasIndexItem { String getRawWord(); /** Returns the substring which is the origin of this item, or null if none */ - public Substring getOrigin(); + Substring getOrigin(); /** Returns the value of this term as a string */ - public abstract String stringValue(); + String stringValue(); /** - * Is this block of text conceptually from the user query? + * Returns whether this block of text is originates from a user and should therefore + * receive the normal processing applied to raw text (such as stemming). */ boolean isFromQuery(); boolean isStemmed(); - /** - * Does this item represent "usual words"? - */ + /** Returns whether this item represents normal text */ boolean isWords(); /** diff --git a/container-search/src/main/java/com/yahoo/prelude/query/CompositeIndexedItem.java b/container-search/src/main/java/com/yahoo/prelude/query/CompositeIndexedItem.java index 14fd7a33232..55adc3cba8d 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/CompositeIndexedItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/CompositeIndexedItem.java @@ -37,9 +37,8 @@ public abstract class CompositeIndexedItem extends CompositeTaggableItem impleme /** Sets the name of the index to search */ public void setIndexName(String index) { - if (index == null) { + if (index == null) index = ""; - } this.index = index; } @@ -51,17 +50,15 @@ public abstract class CompositeIndexedItem extends CompositeTaggableItem impleme } } + @Override public boolean equals(Object object) { - if (!super.equals(object)) { - return false; - } + if (!super.equals(object)) return false; IndexedItem other = (IndexedItem) object; // Ensured by superclass - if (!this.index.equals(other.getIndexName())) { - return false; - } + if (!this.index.equals(other.getIndexName())) return false; return true; } + @Override public int hashCode() { return super.hashCode() + 31 * index.hashCode(); } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/CompositeItem.java b/container-search/src/main/java/com/yahoo/prelude/query/CompositeItem.java index 71d090736fe..aaa4d33c6dc 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/CompositeItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/CompositeItem.java @@ -74,9 +74,8 @@ public abstract class CompositeItem extends Item { * @throws IndexOutOfBoundsException if the index is out of range */ public void addItem(int index, Item item) { - if (index > subitems.size() || index < 0) { + if (index > subitems.size() || index < 0) throw new IndexOutOfBoundsException("Could not add a subitem at position " + index + " to " + this); - } adding(item); subitems.add(index, item); } @@ -97,7 +96,7 @@ public abstract class CompositeItem extends Item { } /** - * Replaces the item at the given index + * Replaces the item at the given index. * * @param index the (0-base) index of the item to replace * @param item the new item @@ -118,7 +117,7 @@ public abstract class CompositeItem extends Item { /** * Returns the index of a subitem * - * @param item The child item to find the index of + * @param item the child item to find the index of * @return the 0-base index of the child or -1 if there is no such child */ public int getItemIndex(Item item) { @@ -218,6 +217,7 @@ public abstract class CompositeItem extends Item { } /** Returns a deep copy of this item */ + @Override public CompositeItem clone() { CompositeItem copy = (CompositeItem) super.clone(); @@ -271,12 +271,11 @@ public abstract class CompositeItem extends Item { return -1; } + @Override public int hashCode() { int code = getName().hashCode() + subitems.size() * 17; - - for (int i = 0; i < subitems.size() && i <= 5; i++) { + for (int i = 0; i < subitems.size() && i <= 5; i++) code += subitems.get(i).hashCode(); - } return code; } @@ -284,17 +283,12 @@ public abstract class CompositeItem extends Item { * Returns whether this item is of the same class and * contains the same state as the given item */ + @Override public boolean equals(Object object) { - if (!super.equals(object)) { - return false; - } + if (!super.equals(object)) return false; CompositeItem other = (CompositeItem) object; // Ensured by superclass - - if (!this.subitems.equals(other.subitems)) { - return false; - } - + if ( ! this.subitems.equals(other.subitems)) return false; return true; } @@ -306,12 +300,30 @@ public abstract class CompositeItem extends Item { return false; } + @Override + public int getTermCount() { + int terms = 0; + for (Item item : subitems) { + terms += item.getTermCount(); + } + return terms; + } + + /** + * Will return its single child if itself can safely be omitted. + * + * @return a valid Item or empty Optional if it can not be done + */ + public Optional extractSingleChild() { + return getItemCount() == 1 ? Optional.of(getItem(0)) : Optional.empty(); + } + /** Handles mutator calls correctly */ private static class ListIteratorWrapper implements ListIterator { - private CompositeItem owner; + private final CompositeItem owner; - private ListIterator wrapped; + private final ListIterator wrapped; private Item current = null; @@ -320,47 +332,54 @@ public abstract class CompositeItem extends Item { wrapped = owner.subitems.listIterator(); } + @Override public boolean hasNext() { return wrapped.hasNext(); } + @Override public Item next() { current = wrapped.next(); return current; } + @Override public boolean hasPrevious() { return wrapped.hasPrevious(); } + @Override public Item previous() { - Item current = wrapped.previous(); - + current = wrapped.previous(); return current; } + @Override public int nextIndex() { return wrapped.nextIndex(); } + @Override public int previousIndex() { return wrapped.previousIndex(); } + @Override public void remove() { owner.removing(current); wrapped.remove(); } + @Override public void set(Item o) { Item newItem = o; - owner.removing(current); owner.adding(newItem); current = newItem; wrapped.set(newItem); } + @Override public void add(Item o) { Item newItem = o; @@ -371,22 +390,4 @@ public abstract class CompositeItem extends Item { } - @Override - public int getTermCount() { - int terms = 0; - for (Item item : subitems) { - terms += item.getTermCount(); - } - return terms; - } - - /** - * Will return its single child if itself can safely be omitted. - * - * @return a valid Item or empty Optional if it can not be done - */ - public Optional extractSingleChild() { - return getItemCount() == 1 ? Optional.of(getItem(0)) : Optional.empty(); - } - } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/CompositeTaggableItem.java b/container-search/src/main/java/com/yahoo/prelude/query/CompositeTaggableItem.java index 46867c2af81..4d1cf1101b5 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/CompositeTaggableItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/CompositeTaggableItem.java @@ -3,7 +3,7 @@ package com.yahoo.prelude.query; /** * Common implementation for Item classes implementing the TaggableItem interface. - * Note that this file exist in 3 copies that should be kept in sync: + * Note that this file exists in 3 copies that should be kept in sync: * * CompositeTaggableItem.java * SimpleTaggableItem.java @@ -28,8 +28,8 @@ public abstract class CompositeTaggableItem extends CompositeItem implements Tag /** See {@link TaggableItem#setConnectivity} */ public void setConnectivity(Item item, double connectivity) { if (!(item instanceof TaggableItem)) { - throw new IllegalArgumentException("setConnectivity item must be taggable, was: " - + item.getClass() + " [" + item + "]"); + throw new IllegalArgumentException("setConnectivity item must be taggable, was: " + + item.getClass() + " [" + item + "]"); } setHasUniqueID(true); item.setHasUniqueID(true); diff --git a/container-search/src/main/java/com/yahoo/prelude/query/ExactStringItem.java b/container-search/src/main/java/com/yahoo/prelude/query/ExactStringItem.java index e313d4e1fe8..cdd1025f3b3 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/ExactStringItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/ExactStringItem.java @@ -17,15 +17,19 @@ public class ExactStringItem extends WordItem { super(substring, isFromQuery); } + @Override public ItemType getItemType() { return ItemType.EXACT; } + @Override public String getName() { return "EXACTSTRING"; } + @Override public String stringValue() { return getWord(); } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/GeoLocationItem.java b/container-search/src/main/java/com/yahoo/prelude/query/GeoLocationItem.java index fd1449d5851..a4aacd9d6a5 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/GeoLocationItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/GeoLocationItem.java @@ -2,7 +2,6 @@ package com.yahoo.prelude.query; -import com.google.common.annotations.Beta; import com.yahoo.prelude.Location; import java.nio.ByteBuffer; @@ -11,14 +10,13 @@ import java.nio.ByteBuffer; * Used for closeness(fieldname) and distance(fieldname) rank features. * @author arnej */ -@Beta public class GeoLocationItem extends TermItem { - private Location location; + private final Location location; /** * Construct from a Location, which must be geo circle with an attribute set. - **/ + */ public GeoLocationItem(Location location) { this(location, location.getAttribute()); if (! location.hasAttribute()) { @@ -30,7 +28,7 @@ public class GeoLocationItem extends TermItem { * Construct from a Location and a field name. * The Location must be a geo circle. * If the Location has an attribute set, it must match the field name. - **/ + */ public GeoLocationItem(Location location, String fieldName) { super(fieldName, false); if (location.hasAttribute() && ! location.getAttribute().equals(fieldName)) { diff --git a/container-search/src/main/java/com/yahoo/prelude/query/HasIndexItem.java b/container-search/src/main/java/com/yahoo/prelude/query/HasIndexItem.java index b137fbcb947..aececf51584 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/HasIndexItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/HasIndexItem.java @@ -4,7 +4,7 @@ package com.yahoo.prelude.query; /** * An interface for items where it is useful to access an index name. * - * @author Steinar Knutsen + * @author Steinar Knutsen */ public interface HasIndexItem { diff --git a/container-search/src/main/java/com/yahoo/prelude/query/Highlight.java b/container-search/src/main/java/com/yahoo/prelude/query/Highlight.java index 44d9d10b603..f393e7601b5 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/Highlight.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/Highlight.java @@ -6,6 +6,7 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import static com.yahoo.language.LinguisticsCase.toLowerCase; @@ -16,9 +17,7 @@ import static com.yahoo.language.LinguisticsCase.toLowerCase; */ public class Highlight implements Cloneable { - /** - * The name of the property map which contains extra highlight terms - */ + /** The name of the property map which contains extra highlight terms */ public static final String HIGHLIGHTTERMS = "highlightterms"; private Map highlightItems = new LinkedHashMap<>(); @@ -39,8 +38,8 @@ public class Highlight implements Cloneable { /** * Add custom highlight term * - * @param field Field name - * @param item Term to be highlighted + * @param field the field name + * @param item the term to be highlighted */ public void addHighlightTerm(String field, String item) { addHighlightItem(field, new WordItem(toLowerCase(item), field, true)); @@ -49,23 +48,18 @@ public class Highlight implements Cloneable { /** * Add custom highlight phrase * - * @param field Field name - * @param phrase List of terms to be highlighted as a phrase + * @param field the field name + * @param phrase the list of terms to be highlighted as a phrase */ public void addHighlightPhrase(String field, List phrase) { PhraseItem pi = new PhraseItem(); pi.setIndexName(field); - for (String s : phrase) { + for (String s : phrase) pi.addItem(new WordItem(toLowerCase(s), field, true)); - } addHighlightItem(field, pi); } - /** - * Returns the modifiable map of highlight items (never null) - * - * @return Map of highlight items - */ + /** Returns the modifiable map of highlight items (never null) */ public Map getHighlightItems() { return highlightItems; } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/IndexedSegmentItem.java b/container-search/src/main/java/com/yahoo/prelude/query/IndexedSegmentItem.java index 1594c6357e2..aaace3043ba 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/IndexedSegmentItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/IndexedSegmentItem.java @@ -4,6 +4,7 @@ package com.yahoo.prelude.query; import com.yahoo.prelude.query.textualrepresentation.Discloser; import java.nio.ByteBuffer; +import java.util.Objects; /** * Common implementation for Item classes implementing the IndexedItem interface. @@ -60,16 +61,14 @@ public abstract class IndexedSegmentItem extends TaggableSegmentItem implements @Override public boolean equals(Object object) { if ( ! super.equals(object)) return false; - - IndexedItem other = (IndexedItem) object; // Ensured by superclass + IndexedItem other = (IndexedItem) object; if ( ! this.index.equals(other.getIndexName())) return false; - return true; } @Override public int hashCode() { - return super.hashCode() + 31 * index.hashCode(); + return Objects.hash(super.hashCode(), index); } public abstract String getIndexedString(); diff --git a/container-search/src/main/java/com/yahoo/prelude/query/IntItem.java b/container-search/src/main/java/com/yahoo/prelude/query/IntItem.java index da2c3e6fa51..5664ecfb3d2 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/IntItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/IntItem.java @@ -4,6 +4,7 @@ package com.yahoo.prelude.query; import java.math.BigInteger; import java.nio.ByteBuffer; +import java.util.Objects; /** @@ -224,7 +225,7 @@ public class IntItem extends TermItem { @Override public int hashCode() { - return super.hashCode() + 199 * expression.hashCode(); + return Objects.hash(super.hashCode(), expression); } @Override diff --git a/container-search/src/main/java/com/yahoo/prelude/query/Item.java b/container-search/src/main/java/com/yahoo/prelude/query/Item.java index 24cfa3a1c5b..e8b1580848d 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/Item.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/Item.java @@ -15,15 +15,10 @@ import java.util.Optional; /** - *

A term of the query language. As "term" is also the common term (sorry) - * for a literal to be found (or not) in a search index, the term item - * is used for query language terms.

- * - *

The query is represented as a composite tree of - * Item subclasses. This allow arbitrary complex combinations of ands, - * nots, phrases and so on.

- * - *

Items are in general mutable and not thread safe.

+ * An item in the tree which defines which documents will match a query. + * Item subclasses can be composed freely to create arbitrary complex matching trees. + * Items are in general mutable and not thread safe. Their identity is defined by their content + * (i.e the field value of two items decide if they are equal). * * @author bratseth * @author havardpe @@ -73,11 +68,6 @@ public abstract class Item implements Cloneable { } - public static final int DEFAULT_WEIGHT = 100; - - /** The relative importance of this term in the query. Default is 100 */ - private int weight = DEFAULT_WEIGHT; - /** * The definitions in Item.ItemCreator must match the ones in * searchlib/src/searchlib/parsequery/parse.h @@ -95,6 +85,11 @@ public abstract class Item implements Cloneable { } + public static final int DEFAULT_WEIGHT = 100; + + /** The relative importance of this term in the query. Default is 100 */ + private int weight = DEFAULT_WEIGHT; + private boolean fromSpecialToken = false; private ItemCreator creator = ItemCreator.ORIG; @@ -390,34 +385,32 @@ public abstract class Item implements Cloneable { } } - /** - * Returns whether this item is of the same class and - * contains the same state as the given item - */ + /** Returns whether this item is of the same class and contains the same state as the given item. */ @Override - public boolean equals(Object object) { - if (object == null) { - return false; - } - if (object.getClass() != this.getClass()) { - return false; - } // Fails on different c.l.'s - - Item other = (Item) object; - - if (this.creator != other.creator) { - return false; - } - if (this.weight != other.weight) { - return false; - } - + public boolean equals(Object o) { + if (o == null) return false; + if (o == this) return true; + if (o.getClass() != this.getClass()) return false; + Item other = (Item)o; + if (this.weight != other.weight) return false; + if (this.fromSpecialToken != other.fromSpecialToken) return false; + if (this.creator != other.creator) return false; + if ( ! Objects.equals(this.annotations, other.annotations)) return false; + if (this.isRanked != other.isRanked) return false; + if (this.usePositionData != other.usePositionData) return false; + if ( ! Objects.equals(this.label, other.label)) return false; + if (this.uniqueID != other.uniqueID) return false; + if ( ! Objects.equals(this.connectedItem, other.connectedItem)) return false; + if (this.connectivity != other.connectivity) return false; + if (this.significance != other.significance) return false; + if (this.language != other.language) return false; return true; } @Override public int hashCode() { - return weight * 29 + creator.code; + return Objects.hash(weight, fromSpecialToken, creator, annotations, isRanked, usePositionData, label, + uniqueID, connectedItem, connectivity, significance, language); } protected boolean hasUniqueID() { diff --git a/container-search/src/main/java/com/yahoo/prelude/query/ItemHelper.java b/container-search/src/main/java/com/yahoo/prelude/query/ItemHelper.java index 1deb3ed1d3c..3c50a87cbe6 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/ItemHelper.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/ItemHelper.java @@ -11,38 +11,6 @@ import java.util.List; */ public class ItemHelper { - /* - We could have exchanged the following 3 functions with this - But this introspection is a bit too much of a hack, so we'll leave it with this. - - - public static T ensureIsItem(Item unknown,Class tClass) { - - if(unknown != null && tClass.isInstance(unknown)) { - return (T) unknown; - } - T item; - - try { - Constructor n = tClass.getConstructor(); - item = n.newInstance(); - } catch (NoSuchMethodException e) { - return null; - } catch (InvocationTargetException e) { - return null; - } catch (IllegalAccessException e) { - return null; - } catch (InstantiationException e) { - return null; - } - if(item != null) { - item.addItem(unknown); - } - return item; - - } - */ - /** Traverse the query tree and return total number of terms */ int getNumTerms(Item rootNode) { int numTerms = 0; diff --git a/container-search/src/main/java/com/yahoo/prelude/query/MarkerWordItem.java b/container-search/src/main/java/com/yahoo/prelude/query/MarkerWordItem.java index 0f22d99a744..75187f8b8e1 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/MarkerWordItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/MarkerWordItem.java @@ -4,6 +4,8 @@ package com.yahoo.prelude.query; import com.yahoo.net.UrlTokenizer; import com.yahoo.prelude.query.textualrepresentation.Discloser; +import java.util.Objects; + /** * Special words known by the index used for marking things. @@ -35,21 +37,14 @@ public class MarkerWordItem extends WordItem { @Override public boolean equals(Object o) { - if (!super.equals(o)) { - return false; - } - if (!(o instanceof MarkerWordItem)) { - return false; - } - + if (!super.equals(o)) return false; MarkerWordItem other = (MarkerWordItem) o; - return markerWord.equals(other.markerWord); } @Override public int hashCode() { - return super.hashCode() + 499 * markerWord.hashCode(); + return Objects.hash(super.hashCode(), markerWord); } @Override diff --git a/container-search/src/main/java/com/yahoo/prelude/query/NearItem.java b/container-search/src/main/java/com/yahoo/prelude/query/NearItem.java index ffc16bfa298..3a2b6c974bf 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/NearItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/NearItem.java @@ -5,6 +5,7 @@ import com.yahoo.compress.IntegerCompressor; import com.yahoo.prelude.query.textualrepresentation.Discloser; import java.nio.ByteBuffer; +import java.util.Objects; /** @@ -76,15 +77,6 @@ public class NearItem extends CompositeItem { buffer.append(" "); } - @Override - public int hashCode() { - return super.hashCode() + 23* distance; - } - - /** - * Returns whether this item is of the same class and - * contains the same state as the given item - */ @Override public boolean equals(Object object) { if (!super.equals(object)) return false; @@ -93,4 +85,9 @@ public class NearItem extends CompositeItem { return true; } + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), distance); + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/NearestNeighborItem.java b/container-search/src/main/java/com/yahoo/prelude/query/NearestNeighborItem.java index 4fe977bff2b..fb19de861ca 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/NearestNeighborItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/NearestNeighborItem.java @@ -6,6 +6,7 @@ import com.yahoo.compress.IntegerCompressor; import com.yahoo.prelude.query.textualrepresentation.Discloser; import java.nio.ByteBuffer; +import java.util.Objects; /** * Represent a query item matching the K nearest neighbors in a multi-dimensional vector space. @@ -33,7 +34,7 @@ public class NearestNeighborItem extends SimpleTaggableItem { /** Returns the K number of hits to produce */ public int getTargetNumHits() { return targetNumHits; } - /** Returns the field name */ + /** Returns the name of the index (field) to be searched */ public String getIndexName() { return field; } /** Returns the distance threshold for nearest-neighbor hits */ @@ -106,4 +107,23 @@ public class NearestNeighborItem extends SimpleTaggableItem { discloser.addProperty("targetHits", targetNumHits); } + @Override + public boolean equals(Object o) { + if ( ! super.equals(o)) return false; + NearestNeighborItem other = (NearestNeighborItem)o; + if (this.targetNumHits != other.targetNumHits) return false; + if (this.hnswExploreAdditionalHits != other.hnswExploreAdditionalHits) return false; + if (this.distanceThreshold != other.distanceThreshold) return false; + if (this.approximate != other.approximate) return false; + if ( ! this.field.equals(other.field)) return false; + if ( ! this.queryTensorName.equals(other.queryTensorName)) return false; + return true; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), targetNumHits, hnswExploreAdditionalHits, + distanceThreshold, approximate, field, queryTensorName); + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/NotItem.java b/container-search/src/main/java/com/yahoo/prelude/query/NotItem.java index 22079cf0666..833b8635f61 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/NotItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/NotItem.java @@ -1,11 +1,9 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.prelude.query; -import com.yahoo.protect.Validator; - -import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Objects; /** * A composite item where the first item is positive and the following @@ -16,18 +14,17 @@ import java.util.List; // TODO: Handle nulls by creating nullItem or checking in encode/toString public class NotItem extends CompositeItem { + @Override public ItemType getItemType() { return ItemType.NOT; } + @Override public String getName() { return "NOT"; } - /** - * Adds an item. The first item is the positive - * the rest is negative - */ + /** Adds an item. The first item is the positive, the rest are negative */ public void addItem(Item item) { super.addItem(item); } @@ -46,10 +43,7 @@ public class NotItem extends CompositeItem { /** Returns the negative items of this: All child items except the first */ public List negativeItems() { return items().subList(1, getItemCount()); } - /** - * Returns the positive item (the first subitem), - * or null if no positive items has been added - */ + /** Returns the positive item (the first subitem), or null if no positive items has been added. */ public Item getPositiveItem() { if (getItemCount() == 0) { return null; @@ -60,10 +54,10 @@ public class NotItem extends CompositeItem { /** * Sets the positive item (the first item) * - * @return the old positive item, or null if there was no items + * @return the old positive item, or null if there was none */ public Item setPositiveItem(Item item) { - Validator.ensureNotNull("Positive item of " + this, item); + Objects.requireNonNull(item, () -> "Positive item of " + this); if (getItemCount() == 0) { addItem(item); return null; diff --git a/container-search/src/main/java/com/yahoo/prelude/query/NullItem.java b/container-search/src/main/java/com/yahoo/prelude/query/NullItem.java index 98a9e242778..1c7716be295 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/NullItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/NullItem.java @@ -15,30 +15,28 @@ public class NullItem extends Item { public NullItem() {} /** Does nothing */ + @Override public void setIndexName(String index) {} + @Override public int encode(ByteBuffer buffer) { - throw new RuntimeException( - "A NullItem was attempted encoded. " - + "This is probably a misbehaving " + "searcher."); + throw new IllegalStateException("A NullItem was attempted encoded. This is probably a misbehaving searcher"); } + @Override public ItemType getItemType() { - throw new RuntimeException( - "Packet code access attempted. " - + "A NullItem has no packet code. " - + "This is probably a misbehaving " + "searcher."); + throw new IllegalStateException("Packet code access attempted. A NullItem has no packet code. " + + "This is probably a misbehaving searcher."); } - public void appendBodyString(StringBuilder buffer) { - // No body for this Item - return; - } + @Override + public void appendBodyString(StringBuilder buffer) {} public void appendHeadingString(StringBuilder buffer) { buffer.append(getName()); } + @Override public String getName() { return "NULL"; } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/ONearItem.java b/container-search/src/main/java/com/yahoo/prelude/query/ONearItem.java index 5f3796e2564..f0f5989d20d 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/ONearItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/ONearItem.java @@ -4,7 +4,6 @@ package com.yahoo.prelude.query; /** * Ordered NearItem. - *

* Matches as a near operator, but also demands that the operands have the * same order in the document as in the query. * diff --git a/container-search/src/main/java/com/yahoo/prelude/query/OrItem.java b/container-search/src/main/java/com/yahoo/prelude/query/OrItem.java index 380993ec7c9..fdc4aabbcd5 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/OrItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/OrItem.java @@ -9,10 +9,12 @@ package com.yahoo.prelude.query; */ public class OrItem extends CompositeItem { + @Override public ItemType getItemType() { return ItemType.OR; } + @Override public String getName() { return "OR"; } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/PhraseItem.java b/container-search/src/main/java/com/yahoo/prelude/query/PhraseItem.java index 00d25c94d67..22c0d9be2d3 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/PhraseItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/PhraseItem.java @@ -43,6 +43,7 @@ public class PhraseItem extends CompositeIndexedItem { return "PHRASE"; } + @Override public void setIndexName(String index) { super.setIndexName(index); for (Iterator i = getItemIterator(); i.hasNext();) { @@ -73,6 +74,7 @@ public class PhraseItem extends CompositeIndexedItem { * * @throws IllegalArgumentException if the given item is not a WordItem or PhraseItem */ + @Override public void addItem(Item item) { if (item instanceof WordItem || item instanceof PhraseSegmentItem || item instanceof WordAlternativesItem) { addIndexedItem((IndexedItem) item); @@ -233,6 +235,7 @@ public class PhraseItem extends CompositeIndexedItem { buffer.append("\""); } + @Override public String getIndexedString() { StringBuilder buf = new StringBuilder(); @@ -251,6 +254,7 @@ public class PhraseItem extends CompositeIndexedItem { return getNumWords(); } + @Override public int getNumWords() { int numWords = 0; for (Iterator j = getItemIterator(); j.hasNext();) { diff --git a/container-search/src/main/java/com/yahoo/prelude/query/PhraseSegmentItem.java b/container-search/src/main/java/com/yahoo/prelude/query/PhraseSegmentItem.java index 9534a5751fd..037a49a3b65 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/PhraseSegmentItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/PhraseSegmentItem.java @@ -63,6 +63,7 @@ public class PhraseSegmentItem extends IndexedSegmentItem { return "SPHRASE"; } + @Override public void setIndexName(String index) { super.setIndexName(index); for (Iterator i = getItemIterator(); i.hasNext();) { @@ -120,6 +121,7 @@ public class PhraseSegmentItem extends IndexedSegmentItem { return (WordItem) getItem(index); } + @Override protected void encodeThis(ByteBuffer buffer) { super.encodeThis(buffer); // takes care of index bytes } @@ -144,6 +146,7 @@ public class PhraseSegmentItem extends IndexedSegmentItem { /** Returns false, no parenthezes for phrases */ + @Override protected boolean shouldParenthize() { return false; } @@ -171,13 +174,6 @@ public class PhraseSegmentItem extends IndexedSegmentItem { buffer.append("'"); } - // TODO: Must check all pertinent items - @Override - public boolean equals(Object object) { - if ( ! super.equals(object)) return false; - return true; - } - @Override public String getIndexedString() { StringBuilder buf = new StringBuilder(); diff --git a/container-search/src/main/java/com/yahoo/prelude/query/PredicateQueryItem.java b/container-search/src/main/java/com/yahoo/prelude/query/PredicateQueryItem.java index da94070c94f..e32c817ceaf 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/PredicateQueryItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/PredicateQueryItem.java @@ -6,6 +6,8 @@ import com.yahoo.compress.IntegerCompressor; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; +import java.util.List; +import java.util.Objects; /** * A PredicateQueryItem is a collection of feature/value-pairs @@ -18,30 +20,26 @@ import java.util.Collection; public class PredicateQueryItem extends SimpleTaggableItem { private String fieldName = "predicate"; - private ArrayList features = new ArrayList<>(); - private ArrayList rangeFeatures = new ArrayList<>(); + private List features = new ArrayList<>(); + private List rangeFeatures = new ArrayList<>(); public static final long ALL_SUB_QUERIES = 0xffffffffffffffffL; - /** - * Sets the field name to be used for the predicates. - * @param index name of the field. - */ + /** Sets the name of the index (field) to be used for the predicates. */ @Override public void setIndexName(String index) { this.fieldName = index; } - /** - * @return the field name used for the predicates. - */ + /** Returns the name of the index (field) used for the predicates. */ public String getIndexName() { return fieldName; } /** * Adds a feature/value-pair to the predicate query. This feature is applied to all sub queries. - * @param key name of the feature to be set in this query. - * @param value value of the feature. + * + * @param key name of the feature to be set in this query + * @param value value of the feature */ public void addFeature(String key, String value) { addFeature(key, value, ALL_SUB_QUERIES); @@ -49,9 +47,10 @@ public class PredicateQueryItem extends SimpleTaggableItem { /** * Adds a feature/value-pair to the predicate query. - * @param key name of the feature to be set in this query. - * @param value value of the feature. - * @param subQueryBitmap bitmap specifying which sub queries this feature applies to. + * + * @param key name of the feature to be set in this query + * @param value value of the feature + * @param subQueryBitmap bitmap specifying which sub queries this feature applies to */ public void addFeature(String key, String value, long subQueryBitmap) { addFeature(new Entry(key, value, subQueryBitmap)); @@ -59,7 +58,8 @@ public class PredicateQueryItem extends SimpleTaggableItem { /** * Adds a feature/value-pair to the predicate query. - * @param entry the feature to add. + * + * @param entry the feature to add */ public void addFeature(Entry entry) { features.add(entry); @@ -68,8 +68,9 @@ public class PredicateQueryItem extends SimpleTaggableItem { /** * Adds a range feature with a given value to the predicate query. * This feature is applied to all sub queries. - * @param key name of the feature to be set in this query. - * @param value value of the feature. + * + * @param key name of the feature to be set in this query + * @param value value of the feature */ public void addRangeFeature(String key, long value) { addRangeFeature(key, value, ALL_SUB_QUERIES); @@ -77,9 +78,10 @@ public class PredicateQueryItem extends SimpleTaggableItem { /** * Adds a range feature with a given value to the predicate query. - * @param key name of the feature to be set in this query. - * @param value value of the feature. - * @param subQueryBitmap bitmap specifying which sub queries this feature applies to. + * + * @param key name of the feature to be set in this query + * @param value value of the feature + * @param subQueryBitmap bitmap specifying which sub queries this feature applies to */ public void addRangeFeature(String key, long value, long subQueryBitmap) { addRangeFeature(new RangeEntry(key, value, subQueryBitmap)); @@ -87,22 +89,19 @@ public class PredicateQueryItem extends SimpleTaggableItem { /** * Adds a range feature with a given value to the predicate query. - * @param entry the feature to add. + * + * @param entry the feature to add */ public void addRangeFeature(RangeEntry entry) { rangeFeatures.add(entry); } - /** - * @return a mutable collection of feature entries. - */ + /** Returns a mutable collection of feature entries. */ public Collection getFeatures() { return features; } - /** - * @return a mutable collection of range feature entries. - */ + /** Returns a mutable collection of range feature entries. */ public Collection getRangeFeatures() { return rangeFeatures; } @@ -126,7 +125,7 @@ public class PredicateQueryItem extends SimpleTaggableItem { return 1; // number of encoded stack dump items } - private void encodeFeatures(ArrayList features, ByteBuffer buffer) { + private void encodeFeatures(List features, ByteBuffer buffer) { IntegerCompressor.putCompressedPositiveNumber(features.size(), buffer); for (EntryBase e : features) { e.encode(buffer); @@ -173,8 +172,24 @@ public class PredicateQueryItem extends SimpleTaggableItem { return clone; } + @Override + public boolean equals(Object o) { + if ( ! super.equals(o)) return false; + var other = (PredicateQueryItem)o; + if ( ! this.fieldName.equals(other.fieldName)) return false; + if ( ! this.features.equals(other.features)) return false; + if ( ! this.rangeFeatures.equals(other.rangeFeatures)) return false; + return true; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), fieldName, features, rangeFeatures); + } + public abstract static class EntryBase { - private String key; + + private final String key; private long subQueryBitmap; public EntryBase(String key, long subQueryBitmap) { @@ -195,10 +210,27 @@ public class PredicateQueryItem extends SimpleTaggableItem { } public abstract void encode(ByteBuffer buffer); + + @Override + public boolean equals(Object o) { + if ( ! super.equals(o)) return false; + + var other = (EntryBase)o; + if ( ! Objects.equals(this.key, other.key)) return false; + if ( this.subQueryBitmap != other.subQueryBitmap) return false; + return true; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), key, subQueryBitmap); + } + } public static class Entry extends EntryBase { - private String value; + + private final String value; public Entry(String key, String value) { this(key, value, ALL_SUB_QUERIES); @@ -218,10 +250,24 @@ public class PredicateQueryItem extends SimpleTaggableItem { putString(getValue(), buffer); buffer.putLong(getSubQueryBitmap()); } + + @Override + public boolean equals(Object other) { + if ( ! super.equals(other)) return false; + if ( ! Objects.equals(this.value, ((Entry)other).value)) return false; + return true; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), value); + } + } public static class RangeEntry extends EntryBase { - private long value; + + private final long value; public RangeEntry(String key, long value) { this(key, value, ALL_SUB_QUERIES); @@ -242,5 +288,19 @@ public class PredicateQueryItem extends SimpleTaggableItem { buffer.putLong(getValue()); buffer.putLong(getSubQueryBitmap()); } + + @Override + public boolean equals(Object other) { + if ( ! super.equals(other)) return false; + if ( this.value != ((RangeEntry)other).value) return false; + return true; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), value); + } + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/PrefixItem.java b/container-search/src/main/java/com/yahoo/prelude/query/PrefixItem.java index e48759323fb..fbe0f2d609c 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/PrefixItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/PrefixItem.java @@ -19,14 +19,17 @@ public class PrefixItem extends WordItem { public PrefixItem(String prefix, String indexName) { super(prefix, indexName); } + @Override public ItemType getItemType() { return ItemType.PREFIX; } + @Override public String getName() { return "PREFIX"; } + @Override public String stringValue() { return getWord() + "*"; } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/PureWeightedInteger.java b/container-search/src/main/java/com/yahoo/prelude/query/PureWeightedInteger.java index 7b93cf30b42..bf8fb2c7d9e 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/PureWeightedInteger.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/PureWeightedInteger.java @@ -2,11 +2,14 @@ package com.yahoo.prelude.query; import java.nio.ByteBuffer; +import java.util.Objects; /** + * An int item which cannot provide its own index (field) name, but will always query the index + * specified by the parent item it is added to. + * * @author baldersheim */ -// TODO: Fix javadoc public class PureWeightedInteger extends PureWeightedItem { private final long value; @@ -40,7 +43,20 @@ public class PureWeightedInteger extends PureWeightedItem { buffer.append(value); super.appendBodyString(buffer); } + public long getValue() { return value; } + + @Override + public boolean equals(Object other) { + if ( ! super.equals(other)) return false; + return value == ((PureWeightedInteger)other).value; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), value); + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/PureWeightedItem.java b/container-search/src/main/java/com/yahoo/prelude/query/PureWeightedItem.java index f29f120f353..cf7cf87434c 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/PureWeightedItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/PureWeightedItem.java @@ -4,14 +4,21 @@ package com.yahoo.prelude.query; import java.nio.ByteBuffer; /** + * An item which cannot provide its own index (field) name, but will always query the index + * specified by the parent item it is added to. + * It's more efficient to use pure items where possible instead of + * {@link TermItem} children ({@link WordItem}, {@link IntItem}) + * who each carry their own index name. + * * @author baldersheim */ -// TODO: Fix javadoc public abstract class PureWeightedItem extends Item { public PureWeightedItem(int weight) { setWeight(weight); } + + /** Ignored. */ @Override public void setIndexName(String index) { // No index @@ -19,7 +26,7 @@ public abstract class PureWeightedItem extends Item { @Override public String getName() { - return getItemType().name(); //To change body of implemented methods use File | Settings | File Templates. + return getItemType().name(); } @Override @@ -32,4 +39,5 @@ public abstract class PureWeightedItem extends Item { protected void appendBodyString(StringBuilder buffer) { buffer.append(':').append(getWeight()); } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/PureWeightedString.java b/container-search/src/main/java/com/yahoo/prelude/query/PureWeightedString.java index 409915fbd1e..58171070678 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/PureWeightedString.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/PureWeightedString.java @@ -2,11 +2,14 @@ package com.yahoo.prelude.query; import java.nio.ByteBuffer; +import java.util.Objects; /** + * A word item which cannot provide its own index (field) name, but will always query the index + * specified by the parent item it is added to. + * * @author baldersheim */ -// TODO: Fix javadoc public class PureWeightedString extends PureWeightedItem { private final String value; @@ -14,6 +17,7 @@ public class PureWeightedString extends PureWeightedItem { public PureWeightedString(String value) { this(value, 100); } + public PureWeightedString(String value, int weight) { super(weight); this.value = value; @@ -44,4 +48,16 @@ public class PureWeightedString extends PureWeightedItem { public String getString() { return value; } + + @Override + public boolean equals(Object other) { + if ( ! super.equals(other)) return false; + return Objects.equals(value, ((PureWeightedString)other).value); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), value); + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/QueryCanonicalizer.java b/container-search/src/main/java/com/yahoo/prelude/query/QueryCanonicalizer.java index 77814c01213..1f30833b3db 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/QueryCanonicalizer.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/QueryCanonicalizer.java @@ -30,7 +30,7 @@ public class QueryCanonicalizer { } /** - * Canonicalize this query + * Canonicalizes this query * * @return null if the query is valid, an error message if it is invalid */ diff --git a/container-search/src/main/java/com/yahoo/prelude/query/RangeItem.java b/container-search/src/main/java/com/yahoo/prelude/query/RangeItem.java index 557c86ffaa1..afe660e58a5 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/RangeItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/RangeItem.java @@ -109,5 +109,4 @@ public class RangeItem extends IntItem { return getToLimit().number(); } - } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/RankItem.java b/container-search/src/main/java/com/yahoo/prelude/query/RankItem.java index 094fa4bb748..d2ae41ff726 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/RankItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/RankItem.java @@ -14,10 +14,12 @@ package com.yahoo.prelude.query; */ public class RankItem extends CompositeItem { + @Override public ItemType getItemType() { return ItemType.RANK; } + @Override public String getName() { return "RANK"; } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/SameElementItem.java b/container-search/src/main/java/com/yahoo/prelude/query/SameElementItem.java index a02991db0ae..ba884d530c8 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/SameElementItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/SameElementItem.java @@ -5,6 +5,7 @@ import com.yahoo.protect.Validator; import java.nio.ByteBuffer; import java.util.Iterator; +import java.util.Objects; import java.util.Optional; /** @@ -75,5 +76,18 @@ public class SameElementItem extends NonReducibleCompositeItem { public String getName() { return getItemType().toString(); } + public String getFieldName() { return fieldName; } + + @Override + public boolean equals(Object other) { + if ( ! super.equals(other)) return false; + return Objects.equals(this.fieldName, ((SameElementItem)other).fieldName); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), fieldName); + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/SegmentItem.java b/container-search/src/main/java/com/yahoo/prelude/query/SegmentItem.java index e2f0ece5501..d02a98486a0 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/SegmentItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/SegmentItem.java @@ -4,6 +4,8 @@ package com.yahoo.prelude.query; import com.yahoo.prelude.query.textualrepresentation.Discloser; +import java.util.Objects; + /** * An immutable and'ing of a collection of sub-expressions. It does not @@ -15,13 +17,13 @@ import com.yahoo.prelude.query.textualrepresentation.Discloser; public abstract class SegmentItem extends CompositeItem implements BlockItem { private boolean locked = false; - private String rawWord; - private String value; - private boolean isFromQuery; + private final String rawWord; + private final String value; + private final boolean isFromQuery; private boolean isFromUser; - private boolean stemmed; + private final boolean stemmed; private SegmentingRule segmentingRule = SegmentingRule.LANGUAGE_DEFAULT; - private Substring origin; + private final Substring origin; /** * Creates a new segment item @@ -42,7 +44,7 @@ public abstract class SegmentItem extends CompositeItem implements BlockItem { * @param current the current transformed version of the raw form, or the raw form repeated if no normalized form is known * @param isFromQuery whether this segment stems from the query received in the request * @param stemmed whether this is stemmed - * @param origin TODO + * @param origin the original text that led to this */ public SegmentItem(String rawWord, String current, boolean isFromQuery, boolean stemmed, Substring origin) { this.rawWord = rawWord; @@ -66,10 +68,12 @@ public abstract class SegmentItem extends CompositeItem implements BlockItem { return value; } + @Override public boolean isFromQuery() { return isFromQuery; } + @Override public boolean isStemmed() { return stemmed; } @@ -87,40 +91,32 @@ public abstract class SegmentItem extends CompositeItem implements BlockItem { return getItemCount(); } + @Override public void addItem(Item item) { - if (locked) { - dontAdd(); - } + throwIfLocked(); super.addItem(item); } + @Override public void addItem(int index, Item item) { - if (locked) { - dontAdd(); - } + throwIfLocked(); super.addItem(index, item); } - private void dontAdd() { - throw new IllegalArgumentException("Tried to add item to an immutable segment."); - } - + @Override public Item removeItem(int index) { - if (locked) { - dontRemove(); - } + throwIfLocked(); return super.removeItem(index); } public boolean removeItem(Item item) { - if (locked) { - dontRemove(); - } + throwIfLocked(); return super.removeItem(item); } - private void dontRemove() { - throw new IllegalArgumentException("Tried to remove an item from an immutable segment."); + private void throwIfLocked() { + if (locked) + throw new IllegalStateException("Cannot change change an immutable segment"); } // TODO: Add a getItemIterator which is safe for immutability @@ -140,6 +136,7 @@ public abstract class SegmentItem extends CompositeItem implements BlockItem { return copy; } + @Override public boolean isWords() { return true; } @@ -175,4 +172,24 @@ public abstract class SegmentItem extends CompositeItem implements BlockItem { public void setSegmentingRule(SegmentingRule segmentingRule) { this.segmentingRule = segmentingRule; } + + @Override + public boolean equals(Object o) { + if ( ! super.equals(o)) return false; + var other = (SegmentItem)o; + if ( ! Objects.equals(this.rawWord, other.rawWord)) return false; + if ( ! Objects.equals(this.value, other.value)) return false; + if ( this.isFromQuery != other.isFromQuery) return false; + if ( this.isFromUser != other.isFromUser) return false; + if ( this.stemmed != other.stemmed) return false; + if ( this.segmentingRule != other.segmentingRule) return false; + if ( ! Objects.equals(this.origin, other.origin)) return false; + return true; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), rawWord, value, isFromQuery, isFromUser, stemmed, segmentingRule, origin); + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/SimpleIndexedItem.java b/container-search/src/main/java/com/yahoo/prelude/query/SimpleIndexedItem.java index 880add231ac..00a7c5cf9b7 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/SimpleIndexedItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/SimpleIndexedItem.java @@ -4,6 +4,7 @@ package com.yahoo.prelude.query; import com.yahoo.prelude.query.textualrepresentation.Discloser; import java.nio.ByteBuffer; +import java.util.Objects; /** * Common implementation for Item classes implementing the IndexedItem interface. @@ -61,7 +62,7 @@ public abstract class SimpleIndexedItem extends SimpleTaggableItem implements In @Override public int hashCode() { - return super.hashCode() + 113 * index.hashCode(); + return Objects.hash(super.hashCode(), index); } public abstract String getIndexedString(); diff --git a/container-search/src/main/java/com/yahoo/prelude/query/Substring.java b/container-search/src/main/java/com/yahoo/prelude/query/Substring.java index fc77d8297d2..599805260fa 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/Substring.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/Substring.java @@ -1,12 +1,13 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.prelude.query; +import java.util.Objects; /** * An substring which also provides access to the full (query) string it is a substring of. - * This is immutable. + * This is a value object. * - * @author bratseth + * @author bratseth */ public class Substring { @@ -66,4 +67,19 @@ public class Substring { return "(" + start + ' ' + end + ')'; } + @Override + public boolean equals(Object o) { + if ( ! (o instanceof Substring)) return false; + var other = (Substring)o; + if (this.start != other.start) return false; + if (this.end != other.end) return false; + if (! Objects.equals(this.string, other.string)) return false; + return true; + } + + @Override + public int hashCode() { + return Objects.hash(start, end, string); + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/SubstringItem.java b/container-search/src/main/java/com/yahoo/prelude/query/SubstringItem.java index 21dfe979ab9..cf3dfba207d 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/SubstringItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/SubstringItem.java @@ -1,7 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.prelude.query; - /** * A word that matches substrings of words * @@ -17,14 +16,17 @@ public class SubstringItem extends WordItem { super(substring, isFromQuery); } + @Override public ItemType getItemType() { return ItemType.SUBSTRING; } + @Override public String getName() { return "SUBSTRING"; } + @Override public String stringValue() { return "*" + getWord() + "*"; } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/SuffixItem.java b/container-search/src/main/java/com/yahoo/prelude/query/SuffixItem.java index d1b0952da9c..5890a3d1000 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/SuffixItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/SuffixItem.java @@ -1,7 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.prelude.query; - /** * A word that matches a suffix of words instead of a complete word. * @@ -17,14 +16,17 @@ public class SuffixItem extends WordItem { super(suffix, isFromQuery); } + @Override public ItemType getItemType() { return ItemType.SUFFIX; } + @Override public String getName() { return "SUFFIX"; } + @Override public String stringValue() { return "*" + getWord(); } 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 ac41544224b..cab1b810c0f 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 @@ -1,7 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. 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. diff --git a/container-search/src/main/java/com/yahoo/prelude/query/TaggableSegmentItem.java b/container-search/src/main/java/com/yahoo/prelude/query/TaggableSegmentItem.java index ebd39e1f72c..91fb91a31b0 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/TaggableSegmentItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/TaggableSegmentItem.java @@ -20,16 +20,19 @@ public abstract class TaggableSegmentItem extends SegmentItem implements Taggabl super(rawWord, current, isFromQuery, stemmed, origin); } + @Override public int getUniqueID() { return uniqueID; } + @Override public void setUniqueID(int id) { setHasUniqueID(true); uniqueID = id; } /** See {@link TaggableItem#setConnectivity} */ + @Override public void setConnectivity(Item item, double connectivity) { if (!(item instanceof TaggableItem)) { throw new IllegalArgumentException("setConnectivity item must be taggable, was: " @@ -46,34 +49,42 @@ public abstract class TaggableSegmentItem extends SegmentItem implements Taggabl connectedItem.connectedBacklink = this; } + @Override public Item getConnectedItem() { return connectedItem; } + @Override public double getConnectivity() { return connectivity; } + @Override public void setSignificance(double significance) { setHasUniqueID(true); setExplicitSignificance(true); this.significance = significance; } + @Override public void setExplicitSignificance(boolean explicitSignificance) { this.explicitSignificance = explicitSignificance; } + @Override public boolean hasExplicitSignificance() { return explicitSignificance; } + @Override public double getSignificance() { return significance; } //Change access privilege from protected to public. + @Override public boolean hasUniqueID() { return super.hasUniqueID(); } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/TermItem.java b/container-search/src/main/java/com/yahoo/prelude/query/TermItem.java index 2794c310010..9d74fdbefe5 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/TermItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/TermItem.java @@ -1,11 +1,10 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.prelude.query; - import com.yahoo.prelude.query.textualrepresentation.Discloser; import java.nio.ByteBuffer; - +import java.util.Objects; /** * Superclass of "leaf" conditions containing a single entity which is either matched in a field or not. @@ -16,7 +15,7 @@ import java.nio.ByteBuffer; public abstract class TermItem extends SimpleIndexedItem implements BlockItem { /** Whether the term is from the raw query or is synthetic. */ - private final boolean isFromQuery; + private boolean isFromQuery; /** Whether accent dropping should be performed */ private boolean normalizable = true; @@ -70,6 +69,7 @@ public abstract class TermItem extends SimpleIndexedItem implements BlockItem { * the superstring this substring was a part of, e.g the whole query string. * If this did not originate directly from a user string, this is null. */ + @Override public Substring getOrigin() { return origin; } /** @@ -77,8 +77,13 @@ public abstract class TermItem extends SimpleIndexedItem implements BlockItem { * Only terms from the user should be modified by query rewriters which attempts to improve the * precision or recall of the user's query. */ + @Override public boolean isFromQuery() { return isFromQuery; } + public void setFromQuery(boolean isFromQuery) { + this.isFromQuery = isFromQuery; + } + @Override public abstract boolean isWords(); @@ -112,4 +117,19 @@ public abstract class TermItem extends SimpleIndexedItem implements BlockItem { public void setSegmentingRule(SegmentingRule segmentingRule) { this.segmentingRule = segmentingRule; } + @Override + public boolean equals(Object o) { + if ( ! super.equals(o)) return false; + var other = (TermItem)o; + if ( this.isFromQuery != other.isFromQuery) return false; + if ( this.normalizable != other.normalizable) return false; + if ( this.segmentingRule != other.segmentingRule) return false; + return true; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), isFromQuery, normalizable, segmentingRule); + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/TermType.java b/container-search/src/main/java/com/yahoo/prelude/query/TermType.java index c041472d5aa..309befd80f5 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/TermType.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/TermType.java @@ -10,19 +10,19 @@ package com.yahoo.prelude.query; */ public class TermType { - public static TermType RANK = new TermType("rank", RankItem.class, null, "$"); + public static final TermType RANK = new TermType("rank", RankItem.class, null, "$"); - public static TermType AND = new TermType("and", AndItem.class, null, "+"); + public static final TermType AND = new TermType("and", AndItem.class, null, "+"); - public static TermType OR = new TermType("or", OrItem.class, null, "?"); + public static final TermType OR = new TermType("or", OrItem.class, null, "?"); - public static TermType NOT = new TermType("not", NotItem.class, null, "-"); + public static final TermType NOT = new TermType("not", NotItem.class, null, "-"); - public static TermType PHRASE = new TermType("phrase", PhraseItem.class, null, "\""); + public static final TermType PHRASE = new TermType("phrase", PhraseItem.class, null, "\""); - public static TermType EQUIV = new TermType("equiv", EquivItem.class, null, ""); + public static final TermType EQUIV = new TermType("equiv", EquivItem.class, null, ""); - public static TermType DEFAULT = new TermType("", CompositeItem.class, AndItem.class, ""); + public static final TermType DEFAULT = new TermType("", CompositeItem.class, AndItem.class, ""); public final String name; diff --git a/container-search/src/main/java/com/yahoo/prelude/query/UriItem.java b/container-search/src/main/java/com/yahoo/prelude/query/UriItem.java index b8d7ea7aa11..86484f9a07a 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/UriItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/UriItem.java @@ -4,6 +4,7 @@ package com.yahoo.prelude.query; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -78,4 +79,19 @@ public class UriItem extends PhraseItem { return items.stream().map(item -> ((WordItem)item).getWord()).collect(Collectors.joining(" ")); } + @Override + public boolean equals(Object o) { + if ( ! super.equals(o)) return false; + var other = (UriItem)o; + if ( this.startAnchorDefault != other.startAnchorDefault) return false; + if ( this.endAnchorDefault != other.endAnchorDefault) return false; + if ( ! Objects.equals(this.sourceString, other.sourceString)) return false; + return true; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), startAnchorDefault, endAnchorDefault, sourceString); + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/WandItem.java b/container-search/src/main/java/com/yahoo/prelude/query/WandItem.java index cb71e0b90bf..8aef14a11b8 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/WandItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/WandItem.java @@ -6,6 +6,7 @@ import com.yahoo.prelude.query.textualrepresentation.Discloser; import java.nio.ByteBuffer; import java.util.Map; +import java.util.Objects; /** * A weighted set query item to be evaluated as a Wand with dot product scoring. @@ -113,4 +114,19 @@ public class WandItem extends WeightedSetItem { discloser.addProperty("thresholdBoostFactor", thresholdBoostFactor); } + @Override + public boolean equals(Object o) { + if ( ! super.equals(o)) return false; + var other = (WandItem)o; + if ( this.targetNumHits != other.targetNumHits) return false; + if ( this.scoreThreshold != other.scoreThreshold) return false; + if ( this.thresholdBoostFactor != other.thresholdBoostFactor) return false; + return false; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), targetNumHits, scoreThreshold, thresholdBoostFactor); + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/WeakAndItem.java b/container-search/src/main/java/com/yahoo/prelude/query/WeakAndItem.java index 393a498dd3e..5ffd4d1584d 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/WeakAndItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/WeakAndItem.java @@ -5,6 +5,7 @@ import com.yahoo.compress.IntegerCompressor; import com.yahoo.prelude.query.textualrepresentation.Discloser; import java.nio.ByteBuffer; +import java.util.Objects; /** * Weak And of a collection of sub-expressions: @@ -53,6 +54,10 @@ public final class WeakAndItem extends NonReducibleCompositeItem { @Override public String getName() { return "WEAKAND"; } + /** + * Sets the default index name to apply to all child items of this. + * This is useful in conjunction with using {@link PureWeightedItem}s as children. + */ @Override public void setIndexName(String index) { String toSet = (index == null) ? "" : index; @@ -60,6 +65,7 @@ public final class WeakAndItem extends NonReducibleCompositeItem { this.index = toSet; } + /** Returns the index name set for this, or null if none. */ public String getIndexName() { return index; } /** Appends the heading of this string - [getName()]([limit]) */ @@ -100,7 +106,7 @@ public final class WeakAndItem extends NonReducibleCompositeItem { } @Override - public int hashCode() { return super.hashCode() + 31 * n; } + public int hashCode() { return Objects.hash(super.hashCode(), n, index); } /** Returns whether this item is of the same class and contains the same state as the given item. */ @Override @@ -108,6 +114,7 @@ public final class WeakAndItem extends NonReducibleCompositeItem { if (!super.equals(object)) return false; WeakAndItem other = (WeakAndItem) object; // Ensured by superclass if (this.n != other.n) return false; + if ( ! Objects.equals(this.index, other.index)) return false; return true; } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/WeightedSetItem.java b/container-search/src/main/java/com/yahoo/prelude/query/WeightedSetItem.java index d7a07824251..907055d435b 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/WeightedSetItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/WeightedSetItem.java @@ -8,6 +8,7 @@ import com.yahoo.prelude.query.textualrepresentation.Discloser; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.Map; +import java.util.Objects; /** * A term which contains a weighted set. @@ -26,7 +27,7 @@ import java.util.Map; public class WeightedSetItem extends SimpleTaggableItem { private String indexName; - private CopyOnWriteHashMap set; + private CopyOnWriteHashMap set; /** Creates an empty weighted set; note you must provide an index name up front */ public WeightedSetItem(String indexName) { @@ -51,11 +52,11 @@ public class WeightedSetItem extends SimpleTaggableItem { } /** - * Add weighted token. - * If token is already in the set, the maximum weight is kept. - * NOTE: The weight must be 1 or more; negative values (and zero) are not allowed. + * Adds a weighted token. + * If this token is already in the set, the maximum weight is kept. + * The weight must be 1 or more; negative values (and zero) are not allowed. * - * @return weight of added token (might be old value, if kept) + * @return the weight of the added token (might be the old value, if kept) */ public Integer addToken(String token, int weight) { if (token == null) throw new IllegalArgumentException("token must be a string"); @@ -72,9 +73,7 @@ public class WeightedSetItem extends SimpleTaggableItem { return newWeight; } - /** - * Add token with weight 1. - */ + /** Adds a token with weight 1. */ public Integer addToken(String token) { return addToken(token, 1); } @@ -180,4 +179,18 @@ public class WeightedSetItem extends SimpleTaggableItem { return clone; } + @Override + public boolean equals(Object o) { + if ( ! super.equals(o)) return false; + var other = (WeightedSetItem)o; + if ( ! Objects.equals(this.indexName, other.indexName)) return false; + if ( ! Objects.equals(this.set, other.set)) return false; + return false; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), indexName, set); + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/WordAlternativesItem.java b/container-search/src/main/java/com/yahoo/prelude/query/WordAlternativesItem.java index 3b22bdbb163..1e9a27f237d 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/WordAlternativesItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/WordAlternativesItem.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import com.google.common.collect.ImmutableList; @@ -20,26 +21,6 @@ public class WordAlternativesItem extends TermItem { private List alternatives; - public static final class Alternative { - - public final String word; - public final double exactness; - - public Alternative(String word, double exactness) { - super(); - this.word = word; - this.exactness = exactness; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("Alternative [word=").append(word).append(", exactness=").append(exactness).append("]"); - return builder.toString(); - } - - } - public WordAlternativesItem(String indexName, boolean isFromQuery, Substring origin, Collection terms) { super(indexName, isFromQuery, origin); setAlternatives(terms); @@ -159,4 +140,48 @@ public class WordAlternativesItem extends TermItem { setAlternatives(newTerms); } + @Override + public boolean equals(Object other) { + if ( ! super.equals(other)) return false; + return this.alternatives.equals(((WordAlternativesItem)other).alternatives); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), alternatives); + } + + /** A word alternative. This is a value object. */ + public static final class Alternative { + + public final String word; + public final double exactness; + + public Alternative(String word, double exactness) { + super(); + this.word = word; + this.exactness = exactness; + } + + @Override + public String toString() { + return "Alternative [word=" + word + ", exactness=" + exactness + "]"; + } + + @Override + public boolean equals(Object o) { + if ( ! (o instanceof Alternative)) return false; + var other = (Alternative)o; + if ( ! Objects.equals(this.word, other.word)) return false; + if (this.exactness != other.exactness) return false; + return true; + } + + @Override + public int hashCode() { + return Objects.hash(word, exactness); + } + + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/WordItem.java b/container-search/src/main/java/com/yahoo/prelude/query/WordItem.java index aa6e9b75fcb..4a2fc69a912 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/WordItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/WordItem.java @@ -1,12 +1,12 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.prelude.query; - import com.yahoo.prelude.query.parser.Token; import com.yahoo.prelude.query.textualrepresentation.Discloser; import com.yahoo.protect.Validator; import java.nio.ByteBuffer; +import java.util.Objects; /** * A simple word or token to match in some field. @@ -147,16 +147,21 @@ public class WordItem extends TermItem { protected void appendHeadingString(StringBuilder buffer) {} @Override - public int hashCode() { - return word.hashCode() + 71 * super.hashCode(); + public boolean equals(Object o) { + if ( ! super.equals(o)) return false; + var other = (WordItem)o; + if ( this.words != other.words) return false; + if ( this.stemmed != other.stemmed) return false; + if ( this.fromSegmented != other.fromSegmented) return false; + if ( this.segmentIndex != other.segmentIndex) return false; + if ( ! Objects.equals(this.word, other.word)) return false; + if ( this.lowercased != other.lowercased) return false; + return true; } @Override - public boolean equals(Object object) { - if (!super.equals(object)) return false; - - WordItem other = (WordItem) object; // Ensured by superclass - return this.word.equals(other.word); + public int hashCode() { + return Objects.hash(super.hashCode(), words, stemmed, fromSegmented, segmentIndex, word, lowercased); } @Override -- cgit v1.2.3 From 56bea1297fc83893b955a21d45e3ac04a022ea52 Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Wed, 27 Oct 2021 13:09:00 +0200 Subject: Add missing cloning --- container-search/abi-spec.json | 14 ++- .../src/main/java/com/yahoo/prelude/Location.java | 139 +++++++++++---------- .../main/java/com/yahoo/prelude/query/AndItem.java | 6 +- .../java/com/yahoo/prelude/query/BlockItem.java | 5 +- .../com/yahoo/prelude/query/GeoLocationItem.java | 10 +- .../main/java/com/yahoo/prelude/query/Item.java | 4 +- .../prelude/query/NonReducibleCompositeItem.java | 2 - .../yahoo/prelude/query/PredicateQueryItem.java | 7 +- .../com/yahoo/prelude/query/WeightedSetItem.java | 1 + .../yahoo/prelude/query/WordAlternativesItem.java | 7 ++ .../java/com/yahoo/prelude/test/QueryTestCase.java | 9 ++ 11 files changed, 114 insertions(+), 90 deletions(-) (limited to 'container-search/src/main/java/com/yahoo') diff --git a/container-search/abi-spec.json b/container-search/abi-spec.json index 484f4cf2a0e..b8b2efea47f 100644 --- a/container-search/abi-spec.json +++ b/container-search/abi-spec.json @@ -320,8 +320,7 @@ "methods": [ "public void ()", "public com.yahoo.prelude.query.Item$ItemType getItemType()", - "public java.lang.String getName()", - "public boolean equals(java.lang.Object)" + "public java.lang.String getName()" ], "fields": [] }, @@ -558,11 +557,14 @@ "public void setValue(java.lang.String)", "public int hashCode()", "public boolean equals(java.lang.Object)", + "public com.yahoo.prelude.query.GeoLocationItem clone()", "public java.lang.String getIndexedString()", "protected void encodeThis(java.nio.ByteBuffer)", "public int getNumWords()", "public boolean isStemmed()", - "public boolean isWords()" + "public boolean isWords()", + "public bridge synthetic com.yahoo.prelude.query.Item clone()", + "public bridge synthetic java.lang.Object clone()" ], "fields": [] }, @@ -1093,7 +1095,6 @@ "public void (java.lang.String, long)", "public java.lang.String getKey()", "public long getSubQueryBitmap()", - "public void setSubQueryBitmap(long)", "public abstract void encode(java.nio.ByteBuffer)", "public boolean equals(java.lang.Object)", "public int hashCode()" @@ -1792,8 +1793,11 @@ "public java.util.List getAlternatives()", "public void encodeThis(java.nio.ByteBuffer)", "public void addTerm(java.lang.String, double)", + "public com.yahoo.prelude.query.WordAlternativesItem clone()", "public boolean equals(java.lang.Object)", - "public int hashCode()" + "public int hashCode()", + "public bridge synthetic com.yahoo.prelude.query.Item clone()", + "public bridge synthetic java.lang.Object clone()" ], "fields": [] }, diff --git a/container-search/src/main/java/com/yahoo/prelude/Location.java b/container-search/src/main/java/com/yahoo/prelude/Location.java index d3c4daab9a0..c686e869536 100644 --- a/container-search/src/main/java/com/yahoo/prelude/Location.java +++ b/container-search/src/main/java/com/yahoo/prelude/Location.java @@ -8,11 +8,12 @@ import java.util.StringTokenizer; /** * Location data for a geographical query. + * This is mutable and clonable. It's identifty is decided by its content. * * @author Steinar Knutsen * @author arnej27959 */ -public class Location { +public class Location implements Cloneable { // 1 or 2 private int dimensions = 0; @@ -34,67 +35,46 @@ public class Location { private String attribute; - public boolean equals(Object other) { - if (! (other instanceof Location)) return false; - Location l = (Location)other; - return dimensions == l.dimensions - && renderCircle == l.renderCircle - && renderRectangle == l.renderRectangle - && this.aspect == l.aspect - && this.x1 == l.x1 - && this.x2 == l.x2 - && this.y1 == l.y1 - && this.y2 == l.y2 - && this.x == l.x - && this.y == l.y - && this.r == l.r; - } - public boolean hasDimensions() { return dimensions != 0; } + public void setDimensions(int d) { - if (hasDimensions() && dimensions != d) { - throw new IllegalArgumentException("already has dimensions="+dimensions+", cannot change it to "+d); - } - if (d == 2) { + if (hasDimensions() && dimensions != d) + throw new IllegalStateException("already has dimensions " + dimensions + ", cannot change to " + d); + if (d == 2) dimensions = d; - } else { - throw new IllegalArgumentException("Illegal location, dimensions must be 2, but was: "+d); - } + else + throw new IllegalArgumentException("Illegal location, dimensions must be 2, but was: " + d); } + public int getDimensions() { return dimensions; } // input data are degrees n/e (if positive) or s/w (if negative) - public void setBoundingBox(double n, double s, - double e, double w) - { + public void setBoundingBox(double n, double s, double e, double w) { setDimensions(2); - if (hasBoundingBox()) { - throw new IllegalArgumentException("can only set bounding box once"); - } + if (hasBoundingBox()) + throw new IllegalStateException("Can only set bounding box once"); int px1 = (int) (Math.round(w * 1000000)); int px2 = (int) (Math.round(e * 1000000)); int py1 = (int) (Math.round(s * 1000000)); int py2 = (int) (Math.round(n * 1000000)); - if (px1 > px2) { - throw new IllegalArgumentException("cannot have w > e"); - } + if (px1 > px2) + throw new IllegalArgumentException("Cannot have w > e"); this.x1 = px1; this.x2 = px2; - if (py1 > py2) { - throw new IllegalArgumentException("cannot have s > n"); - } + if (py1 > py2) + throw new IllegalArgumentException("Cannot have s > n"); this.y1 = py1; this.y2 = py2; renderRectangle = true; } private void adjustAspect() { - //calculate aspect based on latitude (elevation angle) - //no need to "optimize" for special cases, exactly 0, 30, 45, 60, or 90 degrees won't be input anyway + // calculate aspect based on latitude (elevation angle) + // no need to "optimize" for special cases, exactly 0, 30, 45, 60, or 90 degrees won't be input anyway double degrees = (double) y / 1000000d; if (degrees <= -90.0 || degrees >= +90.0) { this.aspect = 0; @@ -107,21 +87,17 @@ public class Location { public void setGeoCircle(double ns, double ew, double radius_in_degrees) { setDimensions(2); - if (isGeoCircle()) { - throw new IllegalArgumentException("can only set geo circle once"); - } + if (isGeoCircle()) + throw new IllegalStateException("Can only set geo circle once"); int px = (int) (ew * 1000000); int py = (int) (ns * 1000000); int pr = (int) (radius_in_degrees * 1000000); - if (ew < -180.1 || ew > +180.1) { + if (ew < -180.1 || ew > +180.1) throw new IllegalArgumentException("e/w location must be in range [-180,+180]"); - } - if (ns < -90.1 || ns > +90.1) { + if (ns < -90.1 || ns > +90.1) throw new IllegalArgumentException("n/s location must be in range [-90,+90]"); - } - if (radius_in_degrees < 0) { + if (radius_in_degrees < 0) pr = -1; - } this.x = px; this.y = py; this.r = pr; @@ -131,12 +107,10 @@ public class Location { public void setXyCircle(int px, int py, int radius_in_units) { setDimensions(2); - if (isGeoCircle()) { - throw new IllegalArgumentException("can only set geo circle once"); - } - if (radius_in_units < 0) { + if (isGeoCircle()) + throw new IllegalStateException("can only set geo circle once"); + if (radius_in_units < 0) radius_in_units = -1; - } this.x = px; this.y = py; this.r = radius_in_units; @@ -145,9 +119,8 @@ public class Location { private void parseRectangle(String rectangle) { int endof = rectangle.indexOf(']'); - if (endof == -1) { - throw new IllegalArgumentException("Illegal location syntax: "+rectangle); - } + if (endof == -1) + throw new IllegalArgumentException("Illegal location syntax: " + rectangle); String rectPart = rectangle.substring(1,endof); StringTokenizer tokens = new StringTokenizer(rectPart, ","); setDimensions(Integer.parseInt(tokens.nextToken())); @@ -155,21 +128,18 @@ public class Location { this.y1 = Integer.parseInt(tokens.nextToken()); this.x2 = Integer.parseInt(tokens.nextToken()); this.y2 = Integer.parseInt(tokens.nextToken()); - if (tokens.hasMoreTokens()) { - throw new IllegalArgumentException("Illegal location syntax: "+rectangle); - } + if (tokens.hasMoreTokens()) + throw new IllegalArgumentException("Illegal location syntax: " + rectangle); renderRectangle = true; String theRest = rectangle.substring(endof+1).trim(); - if (theRest.length() >= 15 && theRest.charAt(0) == '(') { + if (theRest.length() >= 15 && theRest.charAt(0) == '(') parseCircle(theRest); - } } private void parseCircle(String circle) { int endof = circle.indexOf(')'); - if (endof == -1) { - throw new IllegalArgumentException("Illegal location syntax: "+circle); - } + if (endof == -1) + throw new IllegalArgumentException("Illegal location syntax: " + circle); String circlePart = circle.substring(1,endof); StringTokenizer tokens = new StringTokenizer(circlePart, ","); setDimensions(Integer.parseInt(tokens.nextToken())); @@ -187,18 +157,18 @@ public class Location { try { aspect = Long.parseLong(aspectToken); } catch (NumberFormatException nfe) { - throw new IllegalArgumentException("Aspect "+aspectToken+" for location must be an integer or 'CalcLatLon' for automatic aspect calculation.", nfe); - } - if (aspect > 4294967295L || aspect < 0) { - throw new IllegalArgumentException("Aspect "+aspect+" for location parameter must be less than 4294967296 (2^32)"); + throw new IllegalArgumentException("Aspect "+aspectToken+" for location must be an integer or " + + "'CalcLatLon' for automatic aspect calculation.", nfe); } + if (aspect > 4294967295L || aspect < 0) + throw new IllegalArgumentException("Aspect " + aspect + " for location parameter must be " + + "less than 4294967296 (2^32)"); } } renderCircle = true; String theRest = circle.substring(endof+1).trim(); - if (theRest.length() > 5 && theRest.charAt(0) == '[') { + if (theRest.length() > 5 && theRest.charAt(0) == '[') parseRectangle(theRest); - } } public Location() {} @@ -225,9 +195,11 @@ public class Location { } } + @Override public String toString() { return render(false); } + public String backendString() { return render(true); } @@ -284,6 +256,35 @@ public class Location { } } + @Override + public Location clone() { + try { + return (Location) super.clone(); + } + catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (! (other instanceof Location)) return false; + Location l = (Location)other; + return dimensions == l.dimensions + && renderCircle == l.renderCircle + && renderRectangle == l.renderRectangle + && this.aspect == l.aspect + && this.x1 == l.x1 + && this.x2 == l.x2 + && this.y1 == l.y1 + && this.y2 == l.y2 + && this.x == l.x + && this.y == l.y + && this.r == l.r; + } + + @Override public int hashCode() { return toString().hashCode(); } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/AndItem.java b/container-search/src/main/java/com/yahoo/prelude/query/AndItem.java index d5fbb193cfc..ef571794383 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/AndItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/AndItem.java @@ -9,16 +9,14 @@ package com.yahoo.prelude.query; */ public class AndItem extends CompositeItem { + @Override public ItemType getItemType() { return ItemType.AND; } + @Override public String getName() { return "AND"; } - public boolean equals(Object p) { - return super.equals(p); - } - } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/BlockItem.java b/container-search/src/main/java/com/yahoo/prelude/query/BlockItem.java index c3962c97356..bde5fd2c2f9 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/BlockItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/BlockItem.java @@ -9,10 +9,7 @@ package com.yahoo.prelude.query; */ public interface BlockItem extends HasIndexItem { - /** - * The untransformed raw text from the user serving as base for - * this item. - */ + /** The untransformed raw text from the user serving as base for this item. */ String getRawWord(); /** Returns the substring which is the origin of this item, or null if none */ diff --git a/container-search/src/main/java/com/yahoo/prelude/query/GeoLocationItem.java b/container-search/src/main/java/com/yahoo/prelude/query/GeoLocationItem.java index a4aacd9d6a5..712dbbfc489 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/GeoLocationItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/GeoLocationItem.java @@ -8,11 +8,12 @@ import java.nio.ByteBuffer; /** * This represents a geo-location in the query tree. * Used for closeness(fieldname) and distance(fieldname) rank features. + * * @author arnej */ public class GeoLocationItem extends TermItem { - private final Location location; + private Location location; /** * Construct from a Location, which must be geo circle with an attribute set. @@ -88,6 +89,13 @@ public class GeoLocationItem extends TermItem { return true; } + @Override + public GeoLocationItem clone() { + var clone = (GeoLocationItem)super.clone(); + clone.location = this.location.clone(); + return clone; + } + @Override public String getIndexedString() { return location.toString(); diff --git a/container-search/src/main/java/com/yahoo/prelude/query/Item.java b/container-search/src/main/java/com/yahoo/prelude/query/Item.java index e8b1580848d..47efed323a9 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/Item.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/Item.java @@ -17,7 +17,9 @@ import java.util.Optional; /** * An item in the tree which defines which documents will match a query. * Item subclasses can be composed freely to create arbitrary complex matching trees. - * Items are in general mutable and not thread safe. Their identity is defined by their content + * Items are in general mutable and not thread safe. + * They can be deeply cloned by calling clone(). + * Their identity is defined by their content * (i.e the field value of two items decide if they are equal). * * @author bratseth diff --git a/container-search/src/main/java/com/yahoo/prelude/query/NonReducibleCompositeItem.java b/container-search/src/main/java/com/yahoo/prelude/query/NonReducibleCompositeItem.java index 7483863e459..d9c903de374 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/NonReducibleCompositeItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/NonReducibleCompositeItem.java @@ -6,9 +6,7 @@ import java.util.Optional; /** * A composite item which specifies semantics which are not maintained * if an instance with a single child is replaced by the single child. - *

* Most composites, like AND and OR, are reducible as e.g (AND a) is semantically equal to (a). - *

* This type functions as a marker type for query rewriters. * * @author bratseth diff --git a/container-search/src/main/java/com/yahoo/prelude/query/PredicateQueryItem.java b/container-search/src/main/java/com/yahoo/prelude/query/PredicateQueryItem.java index e32c817ceaf..5b15dd1556f 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/PredicateQueryItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/PredicateQueryItem.java @@ -187,6 +187,7 @@ public class PredicateQueryItem extends SimpleTaggableItem { return Objects.hash(super.hashCode(), fieldName, features, rangeFeatures); } + /** An entry in a predicate item. This is immutable. */ public abstract static class EntryBase { private final String key; @@ -205,10 +206,6 @@ public class PredicateQueryItem extends SimpleTaggableItem { return subQueryBitmap; } - public void setSubQueryBitmap(long subQueryBitmap) { - this.subQueryBitmap = subQueryBitmap; - } - public abstract void encode(ByteBuffer buffer); @Override @@ -228,6 +225,7 @@ public class PredicateQueryItem extends SimpleTaggableItem { } + /** A unique entry in a predicate item. This is immutable. */ public static class Entry extends EntryBase { private final String value; @@ -265,6 +263,7 @@ public class PredicateQueryItem extends SimpleTaggableItem { } + /** A range entry in a predicate item. This is immutable. */ public static class RangeEntry extends EntryBase { private final long value; diff --git a/container-search/src/main/java/com/yahoo/prelude/query/WeightedSetItem.java b/container-search/src/main/java/com/yahoo/prelude/query/WeightedSetItem.java index 907055d435b..84a176c2f7d 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/WeightedSetItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/WeightedSetItem.java @@ -38,6 +38,7 @@ public class WeightedSetItem extends SimpleTaggableItem { } set = new CopyOnWriteHashMap<>(1000); } + public WeightedSetItem(String indexName, Map map) { if (indexName == null) { this.indexName = ""; diff --git a/container-search/src/main/java/com/yahoo/prelude/query/WordAlternativesItem.java b/container-search/src/main/java/com/yahoo/prelude/query/WordAlternativesItem.java index 1e9a27f237d..59dad29ab5c 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/WordAlternativesItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/WordAlternativesItem.java @@ -140,6 +140,13 @@ public class WordAlternativesItem extends TermItem { setAlternatives(newTerms); } + @Override + public WordAlternativesItem clone() { + var clone = (WordAlternativesItem)super.clone(); + clone.alternatives = new ArrayList(this.alternatives); + return clone; + } + @Override public boolean equals(Object other) { if ( ! super.equals(other)) return false; diff --git a/container-search/src/test/java/com/yahoo/prelude/test/QueryTestCase.java b/container-search/src/test/java/com/yahoo/prelude/test/QueryTestCase.java index 526f520a583..d9e3cf84726 100644 --- a/container-search/src/test/java/com/yahoo/prelude/test/QueryTestCase.java +++ b/container-search/src/test/java/com/yahoo/prelude/test/QueryTestCase.java @@ -8,6 +8,7 @@ import com.yahoo.prelude.Index; import com.yahoo.prelude.IndexFacts; import com.yahoo.prelude.IndexModel; import com.yahoo.prelude.SearchDefinition; +import com.yahoo.prelude.query.BoolItem; import com.yahoo.prelude.query.WordItem; import com.yahoo.search.Query; import com.yahoo.search.query.Sorting; @@ -31,6 +32,14 @@ import static org.junit.Assert.*; */ public class QueryTestCase { + @Test + public void testBoolItem() { + var original = new BoolItem(false); + var cloned = original.clone(); + assertNotSame(original, cloned); + assertEquals(original, cloned); + } + @Test public void testSimpleQueryParsing () { Query q = newQuery("/search?query=foobar&offset=10&hits=20"); -- cgit v1.2.3 From 1a13aeafd1ced9e097f3b76469e4afa15e0598a5 Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Thu, 28 Oct 2021 08:30:38 +0200 Subject: Make final --- .../src/main/java/com/yahoo/prelude/query/PredicateQueryItem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'container-search/src/main/java/com/yahoo') diff --git a/container-search/src/main/java/com/yahoo/prelude/query/PredicateQueryItem.java b/container-search/src/main/java/com/yahoo/prelude/query/PredicateQueryItem.java index 5b15dd1556f..91a0ea4eb51 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/PredicateQueryItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/PredicateQueryItem.java @@ -191,7 +191,7 @@ public class PredicateQueryItem extends SimpleTaggableItem { public abstract static class EntryBase { private final String key; - private long subQueryBitmap; + private final long subQueryBitmap; public EntryBase(String key, long subQueryBitmap) { this.key = key; -- cgit v1.2.3 From 888d3db0ea5887d8ba71ff6b84be6762d3cb1586 Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Thu, 28 Oct 2021 08:31:41 +0200 Subject: Correct comment --- container-search/src/main/java/com/yahoo/prelude/query/BlockItem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'container-search/src/main/java/com/yahoo') diff --git a/container-search/src/main/java/com/yahoo/prelude/query/BlockItem.java b/container-search/src/main/java/com/yahoo/prelude/query/BlockItem.java index bde5fd2c2f9..377ed74f672 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/BlockItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/BlockItem.java @@ -19,7 +19,7 @@ public interface BlockItem extends HasIndexItem { String stringValue(); /** - * Returns whether this block of text is originates from a user and should therefore + * Returns whether this block of text originates from a user and should therefore * receive the normal processing applied to raw text (such as stemming). */ boolean isFromQuery(); -- cgit v1.2.3 From 09e599d1f8a1e390075facca4b8b3c5cb8d2097d Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Thu, 28 Oct 2021 08:39:41 +0200 Subject: Check 'explicit' for equality --- .../src/main/java/com/yahoo/prelude/query/PhraseItem.java | 12 ++++++++++++ .../main/java/com/yahoo/prelude/query/PhraseSegmentItem.java | 12 ++++++++++++ 2 files changed, 24 insertions(+) (limited to 'container-search/src/main/java/com/yahoo') diff --git a/container-search/src/main/java/com/yahoo/prelude/query/PhraseItem.java b/container-search/src/main/java/com/yahoo/prelude/query/PhraseItem.java index 22c0d9be2d3..fae282868f8 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/PhraseItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/PhraseItem.java @@ -5,6 +5,7 @@ import com.yahoo.prelude.query.textualrepresentation.Discloser; import java.nio.ByteBuffer; import java.util.Iterator; +import java.util.Objects; import java.util.Optional; /** @@ -269,4 +270,15 @@ public class PhraseItem extends CompositeIndexedItem { discloser.addProperty("explicit", explicit); } + @Override + public boolean equals(Object other) { + if ( ! super.equals(other)) return false; + return this.explicit == ((PhraseItem)other).explicit; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), explicit); + } + } diff --git a/container-search/src/main/java/com/yahoo/prelude/query/PhraseSegmentItem.java b/container-search/src/main/java/com/yahoo/prelude/query/PhraseSegmentItem.java index 037a49a3b65..16e22f6d482 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/PhraseSegmentItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/PhraseSegmentItem.java @@ -5,6 +5,7 @@ import com.yahoo.prelude.query.textualrepresentation.Discloser; import java.nio.ByteBuffer; import java.util.Iterator; +import java.util.Objects; import java.util.Optional; @@ -203,4 +204,15 @@ public class PhraseSegmentItem extends IndexedSegmentItem { discloser.addProperty("explicit", explicit); } + @Override + public boolean equals(Object other) { + if ( ! super.equals(other)) return false; + return this.explicit == ((PhraseSegmentItem)other).explicit; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), explicit); + } + } -- cgit v1.2.3