Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include QueryBuilder implementations #3107

Merged
merged 4 commits into from
Sep 8, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/main/java/redis/clients/jedis/args/GeoUnit.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@ public enum GeoUnit implements Rawable {
M, KM, MI, FT;

private final byte[] raw;
private final String unit;

private GeoUnit() {
raw = SafeEncoder.encode(name().toLowerCase(Locale.ENGLISH));
GeoUnit() {
sazzad16 marked this conversation as resolved.
Show resolved Hide resolved
unit = name().toLowerCase(Locale.ENGLISH);
raw = SafeEncoder.encode(unit);
}

@Override
public byte[] getRaw() {
return raw;
}

@Override
public String toString() {
return unit;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package redis.clients.jedis.search.querybuilder;

/**
* A disjunct node. evaluates to true if any of its children are false. Conversely, this node evaluates to false
* only iff <b>all</b> of its children are true, making it the exact inverse of {@link IntersectNode}
*
* In RS, it looks like:
*
* {@code -(@f1:v1 @f2:v2)}
*
* @see DisjunctUnionNode which evalutes to true if <b>all</b> its children are false.
*/
public class DisjunctNode extends IntersectNode {
@Override
public String toString(ParenMode mode) {
String ret = super.toString(ParenMode.NEVER);
if (shouldUseParens(mode)) {
return "-(" + ret + ")";
} else {
return "-" + ret;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package redis.clients.jedis.search.querybuilder;

/**
* A disjunct union node is the inverse of a {@link UnionNode}. It evaluates to true only iff <b>all</b> its
* children are false. Conversely, it evaluates to false if <b>any</b> of its children are true.
*
* As an RS query it looks like {@code -(@f1:v1|@f2:v2)}
*
* @see DisjunctNode which evaluates to true if <b>any</b> of its children are false.
*/
public class DisjunctUnionNode extends DisjunctNode {
@Override
protected String getJoinString() {
return "|";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package redis.clients.jedis.search.querybuilder;

/**
* @author mnunberg on 2/23/18.
*/
public class DoubleRangeValue extends RangeValue {
private final double from;
private final double to;

private static void appendNum(StringBuilder sb, double n, boolean inclusive) {
if (!inclusive) {
sb.append("(");
}
if (n == Double.NEGATIVE_INFINITY) {
sb.append("-inf");
} else if (n == Double.POSITIVE_INFINITY) {
sb.append("inf");
} else {
sb.append(n);
}
}

public DoubleRangeValue(double from, double to) {
this.from = from;
this.to = to;
}

@Override
protected void appendFrom(StringBuilder sb, boolean inclusive) {
appendNum(sb, from, inclusive);
}

@Override
protected void appendTo(StringBuilder sb, boolean inclusive) {
appendNum(sb, to, inclusive);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package redis.clients.jedis.search.querybuilder;

import redis.clients.jedis.args.GeoUnit;

/**
* Created by mnunberg on 2/23/18.
*/
public class GeoValue extends Value {

private final String unit;
private final double lon;
private final double lat;
private final double radius;

public GeoValue(double lon, double lat, double radius, GeoUnit unit) {
this.lon = lon;
this.lat = lat;
this.radius = radius;
this.unit = unit.toString();
}

@Override
public String toString() {
return "[" + lon + " " + lat + " " + radius + " " + unit + "]";
}
sazzad16 marked this conversation as resolved.
Show resolved Hide resolved

@Override
public boolean isCombinable() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package redis.clients.jedis.search.querybuilder;

/**
* The intersection node evaluates to true if any of its children are true.
*
* In RS: {@code @f1:v1 @f2:v2}
*/
public class IntersectNode extends QueryNode {
@Override
protected String getJoinString() {
return " ";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package redis.clients.jedis.search.querybuilder;

public class LongRangeValue extends RangeValue {
private final long from;
private final long to;

@Override
public boolean isCombinable() {
return false;
}

private static void appendNum(StringBuilder sb, long n, boolean inclusive) {
if (!inclusive) {
sb.append("(");
}
if (n == Long.MIN_VALUE) {
sb.append("-inf");
} else if (n == Long.MAX_VALUE) {
sb.append("inf");
} else {
sb.append(Long.toString(n));
}
}

public LongRangeValue(long from, long to) {
this.from = from;
this.to = to;
}

@Override
protected void appendFrom(StringBuilder sb, boolean inclusive) {
appendNum(sb, from, inclusive);
}

@Override
protected void appendTo(StringBuilder sb, boolean inclusive) {
appendNum(sb, to, inclusive);
}
}
39 changes: 39 additions & 0 deletions src/main/java/redis/clients/jedis/search/querybuilder/Node.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package redis.clients.jedis.search.querybuilder;

import redis.clients.jedis.search.Query;

/**
* Created by mnunberg on 2/23/18.
*
* Base node interface
*/
public interface Node {
enum ParenMode {
sazzad16 marked this conversation as resolved.
Show resolved Hide resolved
/** Always encapsulate */
ALWAYS,

/**
* Never encapsulate. Note that this may be ignored if parentheses are semantically required (e.g.
* {@code @foo:(val1|val2)}. However something like {@code @foo:v1 @bar:v2} need not be parenthesized.
*/
NEVER,

/**
* Determine encapsulation based on number of children. If the node only has one child, it is not
* parenthesized, if it has more than one child, it is parenthesized */
DEFAULT
}

/**
* Returns the string form of this node.
* @param mode Whether the string should be encapsulated in parentheses {@code (...)}
* @return The string query.
*/
String toString(ParenMode mode);

/**
* Returns the string form of this node. This may be passed to {@link redis.clients.jedis.UnifiedJedis#ftSearch(String, Query)}
* @return The query string.
*/
String toString();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package redis.clients.jedis.search.querybuilder;

/**
* Created by mnunberg on 2/23/18.
*
* The optional node affects scoring and ordering. If it evaluates to true, the result is ranked
* higher. It is helpful to combine it with a {@link UnionNode} to rank a document higher if it meets
* one of several criteria.
*
* In RS: {@code ~(@lang:en @country:us)}.
*/
public class OptionalNode extends IntersectNode {
@Override
public String toString(ParenMode mode) {
String ret = super.toString(ParenMode.NEVER);
if (shouldUseParens(mode)) {
return "~(" + ret + ")";
}
return "~" + ret;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package redis.clients.jedis.search.querybuilder;

import java.util.Arrays;

import static redis.clients.jedis.search.querybuilder.Values.value;

/**
* Created by mnunberg on 2/23/18.
*
* This class contains methods to construct query nodes. These query nodes can be added to parent query
* nodes (building a chain) or used as the root query node.
*
* You can use <pre>import static</pre> for these helper methods.
*/
public class QueryBuilder {
sazzad16 marked this conversation as resolved.
Show resolved Hide resolved
private QueryBuilder() {
}
sazzad16 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Create a new intersection node with child nodes. An intersection node is true if all its children
* are also true
* @param n sub-condition to add
* @return The node
*/
public static QueryNode intersect(Node ...n) {
return new IntersectNode().add(n);
}

/**
* Create a new intersection node with a field-value pair.
* @param field The field that should contain this value. If this value is empty, then any field
* will be checked.
* @param values Value to check for. The node will be true only if the field (or any field)
* contains <i>all</i> of the values
* @return The node
*/
public static QueryNode intersect(String field, Value ...values) {
return intersect().add(field, values);
}

/**
* Helper method to create a new intersection node with a string value.
* @param field The field to check. If left null or empty, all fields will be checked.
* @param stringValue The value to check
* @return The node
*/
public static QueryNode intersect(String field, String stringValue) {
return intersect(field, value(stringValue));
}

/**
* Create a union node. Union nodes evaluate to true if <i>any</i> of its children are true
* @param n Child node
* @return The union node
*/
public static QueryNode union(Node ...n) {
return new UnionNode().add(n);
}

/**
* Create a union node which can match an one or more values
* @param field Field to check. If empty, all fields are checked
* @param values Values to search for. The node evaluates to true if {@code field} matches
* any of the values
* @return The union node
*/
public static QueryNode union(String field, Value ...values) {
return union().add(field, values);
}

/**
* Convenience method to match one or more strings. This is equivalent to
* {@code union(field, value(v1), value(v2), value(v3)) ...}
* @param field Field to match
* @param values Strings to check for
* @return The union node
*/
public static QueryNode union(String field, String ...values) {
return union(field, (Value[]) Arrays.stream(values).map(Values::value).toArray());
}

/**
* Create a disjunct node. Disjunct nodes are true iff <b>any</b> of its children are <b>not</b> true.
* Conversely, this node evaluates to false if <b>all</b> its children are true.
* @param n Child nodes to add
* @return The disjunct node
*/
public static QueryNode disjunct(Node ...n) {
return new DisjunctNode().add(n);
}

/**
* Create a disjunct node using one or more values. The node will evaluate to true iff the field does not
* match <b>any</b> of the values.
*
* @param field Field to check for (empty or null for any field)
* @param values The values to check for
* @return The node
*/
public static QueryNode disjunct(String field, Value ...values) {
return disjunct().add(field, values);
}

/**
* Create a disjunct node using one or more values. The node will evaluate to true iff the field does not
* match <b>any</b> of the values.
*
* @param field Field to check for (empty or null for any field)
* @param values The values to check for
* @return The node
*/
public static QueryNode disjunct(String field, String ...values) {
return disjunct(field, (Value[]) Arrays.stream(values).map(Values::value).toArray());
}

/**
* Create a disjunct union node. This node evaluates to true if <b>all</b> of its children are not true.
* Conversely, this node evaluates as false if <b>any</b> of its children are true.
* @param n
* @return The node
*/
public static QueryNode disjunctUnion(Node ...n) {
return new DisjunctUnionNode().add(n);
}

public static QueryNode disjunctUnion(String field, Value ...values) {
return disjunctUnion().add(field, values);
}

public static QueryNode disjunctUnion(String field, String ...values) {
return disjunctUnion(field, (Value[]) Arrays.stream(values).map(Values::value).toArray());
}

/**
* Create an optional node. Optional nodes do not affect which results are returned but they influence
* ordering and scoring.
* @param n The node to evaluate as optional
* @return The new node
*/
public static QueryNode optional(Node ...n) {
return new OptionalNode().add(n);
}
public static QueryNode optional(String field, Value ...values) {
return optional().add(field, values);
}
}
Loading