package com.senseidb.search.query.filters; /** * Estimated cardinality of a DocIdSet. * * We represent estimated cardinality with an uniform distribution from min to * max, where 0 is no documents, and 1.0 is all documents. * * Cardinality is used during query planning. ANDs and ORs short-circuit. If * one test in an AND fails, others are not evaluated. If one test in an OR * matches, others are not evaluated. We therefore want to re-order ANDs by * increasing cardinality (most-selective first), and ORs by decreasing. We * also remove match-all (cardinality 1.0-1.0) terms from ANDs, and match-none * (cardinality 0.0-0.0) terms from ORs. */ public class DocIdSetCardinality implements Cloneable, Comparable<DocIdSetCardinality> { double min; double max; /** * Match-none cardinality, matches 0 docs. */ public static DocIdSetCardinality zero() { return new DocIdSetCardinality(0.0, 0.0); } /** * Match-all cardinality, matches all docs. */ public static DocIdSetCardinality one() { return new DocIdSetCardinality(1.0, 1.0); } /** * Unspecified cardinality, matching anywhere from zero to all docs. */ public static DocIdSetCardinality random() { return new DocIdSetCardinality(0.0, 1.0); } /** * Exact cardinality, matches the given ratio of docs. * * @param cardinality Ratio (0.0-1.0). */ public static DocIdSetCardinality exact(double cardinality) { return new DocIdSetCardinality(cardinality, cardinality); } /** * Exact cardinality, matches the given number of docs. * * @param count Number of matched docs (0-outOf). * @param outOf Total number of docs. */ public static DocIdSetCardinality exact(int count, int outOf) { return exact(((double)count) / outOf); } /** * Exact cardinality range, matching from min to max ratio. * * @param min Minimum ratio (0.0-1.0) * @param max Minimum ratio (0.0-1.0) */ public static DocIdSetCardinality exactRange(double min, double max) { return new DocIdSetCardinality(min, max); } /** * Exact cardinality range, matching from min to max docs. * * @param min Minimum number of matched docs (0-outOf). * @param max Maximum number of matched docs (0-outOf). * @param outOf Total number of docs. */ public static DocIdSetCardinality exactRange(int min, int max, int outOf) { return new DocIdSetCardinality(((double)min) / outOf, ((double)max) / outOf); } /** * Default constructor - do not use. * * Use named static factory functions instead. */ DocIdSetCardinality(double minCardinality, double maxCardinality) { min = Math.min(Math.max(0.0, minCardinality), 1.0); max = Math.min(Math.max(0.0, maxCardinality), 1.0); } /** * AND with another cardinality. * * Example: Presuming 100 docs, one query matches 80-90, the other 70-80. * Result can match no less than 50, presuming the first query matched 80 * (didn't match 20) and the second query matched 70. It cannot match more * than 80 (the smaller of two maxes) */ public void andWith(DocIdSetCardinality other) { min = Math.max(0.0, min + other.min - 1.0); // min - (1.0 - other.min) max = Math.min(max, other.max); } /** * OR with another cardinality. * * Example: Presuming 100 docs, one query matches 0-10, the other 20-30. * Result can match no less than 20 (the larger of two). It cannot match * more than 40 (the sum). */ public void orWith(DocIdSetCardinality other) { min = Math.max(min, other.min); max = Math.min(1.0, max + other.max); } /** * Invert this cardinality. * * Example: if one query matches 50%-100% of documents, it's inverse matches * 0%-50%. */ public void invert() { final double oldMin = min; min = 1.0 - max; max = 1.0 - oldMin; } /** * @return True if this query matches all docs. */ public boolean isOne() { return min >= 1.0 && max >= 1.0; } /** * @return True if this query matches no docs. */ public boolean isZero() { return min <= 0.0 && max <= 0.0; } /** * @return True if we have no cardinality info. */ public boolean isRandom() { return min <= 0.0 && max >= 1.0; } @Override public String toString() { return min + "-" + max; } @Override public DocIdSetCardinality clone() { try { return (DocIdSetCardinality)super.clone(); } catch (CloneNotSupportedException e) { return null; } } /** * Default comparator. * * Compares medians: (min + max)/2. */ @Override public int compareTo(DocIdSetCardinality o) { return (int)Math.signum(min + max - o.min - o.max); } }