Skip to content

Commit dd9fb87

Browse files
committed
fix: addresses inconsistencies in query range generation for Date/Time types (resolves gh-270)
1 parent 8f8f090 commit dd9fb87

File tree

16 files changed

+422
-142
lines changed

16 files changed

+422
-142
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.redis.om.spring.search.stream.predicates.jedis;
2+
3+
import com.google.gson.JsonPrimitive;
4+
import redis.clients.jedis.search.querybuilder.RangeValue;
5+
6+
import java.util.Date;
7+
8+
public class DateRangeValue extends RangeValue {
9+
private final Date from;
10+
private final Date to;
11+
12+
public static Date MIN = new Date(Long.MIN_VALUE);
13+
public static Date MAX = new Date(Long.MAX_VALUE);
14+
15+
public DateRangeValue(Date from, Date to) {
16+
this.from = from;
17+
this.to = to;
18+
}
19+
20+
private static void appendDate(StringBuilder sb, Date date, boolean inclusive) {
21+
if (!inclusive) {
22+
sb.append("(");
23+
}
24+
25+
if (date == MIN) {
26+
sb.append("-inf");
27+
} else if (date == MAX) {
28+
sb.append("inf");
29+
} else {
30+
sb.append(new JsonPrimitive(date.getTime()));
31+
}
32+
33+
}
34+
35+
@Override
36+
protected void appendFrom(StringBuilder sb, boolean inclusive) {
37+
appendDate(sb, this.from, inclusive);
38+
}
39+
40+
@Override
41+
protected void appendTo(StringBuilder sb, boolean inclusive) {
42+
appendDate(sb, this.to, inclusive);
43+
}
44+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.redis.om.spring.search.stream.predicates.jedis;
2+
3+
import com.google.gson.JsonPrimitive;
4+
import redis.clients.jedis.search.querybuilder.RangeValue;
5+
6+
import java.time.Instant;
7+
8+
public class InstantRangeValue extends RangeValue {
9+
private final Instant from;
10+
private final Instant to;
11+
12+
public InstantRangeValue(Instant from, Instant to) {
13+
this.from = from;
14+
this.to = to;
15+
}
16+
17+
private static void appendInstant(StringBuilder sb, Instant instant, boolean inclusive) {
18+
if (!inclusive) {
19+
sb.append("(");
20+
}
21+
22+
if (instant == Instant.MIN) {
23+
sb.append("-inf");
24+
} else if (instant == Instant.MAX) {
25+
sb.append("inf");
26+
} else {
27+
long timeInMillis = instant.getEpochSecond();
28+
sb.append(new JsonPrimitive(timeInMillis));
29+
}
30+
31+
}
32+
33+
@Override
34+
protected void appendFrom(StringBuilder sb, boolean inclusive) {
35+
appendInstant(sb, this.from, inclusive);
36+
}
37+
38+
@Override
39+
protected void appendTo(StringBuilder sb, boolean inclusive) {
40+
appendInstant(sb, this.to, inclusive);
41+
}
42+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package com.redis.om.spring.search.stream.predicates.jedis;
2+
3+
import redis.clients.jedis.search.querybuilder.*;
4+
5+
import java.time.Instant;
6+
import java.time.LocalDate;
7+
import java.time.LocalDateTime;
8+
import java.util.Date;
9+
10+
public class JedisValues {
11+
private JedisValues() {
12+
throw new InstantiationError("Must not instantiate this class");
13+
}
14+
15+
// --------------
16+
// java.util.Date
17+
// --------------
18+
19+
public static RangeValue between(Date from, Date to) {
20+
return new DateRangeValue(from, to);
21+
}
22+
23+
public static RangeValue eq(Date d) {
24+
return new DateRangeValue(d, d);
25+
}
26+
27+
public static RangeValue lt(Date d) {
28+
return (new DateRangeValue(DateRangeValue.MIN, d)).inclusiveMax(false);
29+
}
30+
31+
public static RangeValue gt(Date d) {
32+
return (new DateRangeValue(d, DateRangeValue.MAX)).inclusiveMin(false);
33+
}
34+
35+
public static RangeValue le(Date d) {
36+
return lt(d).inclusiveMax(true);
37+
}
38+
39+
public static RangeValue ge(Date d) {
40+
return gt(d).inclusiveMin(true);
41+
}
42+
43+
// -------------------
44+
// java.time.LocalDate
45+
// -------------------
46+
47+
public static RangeValue between(LocalDate from, LocalDate to) {
48+
return new LocalDateRangeValue(from, to);
49+
}
50+
51+
public static RangeValue eq(LocalDate d) {
52+
return new LocalDateRangeValue(d, d);
53+
}
54+
55+
public static RangeValue lt(LocalDate d) {
56+
return (new LocalDateRangeValue(LocalDate.MIN, d)).inclusiveMax(false);
57+
}
58+
59+
public static RangeValue gt(LocalDate d) {
60+
return (new LocalDateRangeValue(d, LocalDate.MAX)).inclusiveMin(false);
61+
}
62+
63+
public static RangeValue le(LocalDate d) {
64+
return lt(d).inclusiveMax(true);
65+
}
66+
67+
public static RangeValue ge(LocalDate d) {
68+
return gt(d).inclusiveMin(true);
69+
}
70+
71+
// -----------------------
72+
// java.time.LocalDateTime
73+
// -----------------------
74+
75+
public static RangeValue between(LocalDateTime from, LocalDateTime to) {
76+
return new LocalDateTimeRangeValue(from, to);
77+
}
78+
79+
public static RangeValue eq(LocalDateTime d) {
80+
return new LocalDateTimeRangeValue(d, d);
81+
}
82+
83+
public static RangeValue lt(LocalDateTime d) {
84+
return (new LocalDateTimeRangeValue(LocalDateTime.MIN, d)).inclusiveMax(false);
85+
}
86+
87+
public static RangeValue gt(LocalDateTime d) {
88+
return (new LocalDateTimeRangeValue(d, LocalDateTime.MAX)).inclusiveMin(false);
89+
}
90+
91+
public static RangeValue le(LocalDateTime d) {
92+
return lt(d).inclusiveMax(true);
93+
}
94+
95+
public static RangeValue ge(LocalDateTime d) {
96+
return gt(d).inclusiveMin(true);
97+
}
98+
99+
// -----------------
100+
// java.time.Instant
101+
// -----------------
102+
103+
public static RangeValue between(Instant from, Instant to) {
104+
return new InstantRangeValue(from, to);
105+
}
106+
107+
public static RangeValue eq(Instant d) {
108+
return new InstantRangeValue(d, d);
109+
}
110+
111+
public static RangeValue lt(Instant d) {
112+
return (new InstantRangeValue(Instant.MIN, d)).inclusiveMax(false);
113+
}
114+
115+
public static RangeValue gt(Instant d) {
116+
return (new InstantRangeValue(d, Instant.MAX)).inclusiveMin(false);
117+
}
118+
119+
public static RangeValue le(Instant d) {
120+
return lt(d).inclusiveMax(true);
121+
}
122+
123+
public static RangeValue ge(Instant d) {
124+
return gt(d).inclusiveMin(true);
125+
}
126+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.redis.om.spring.search.stream.predicates.jedis;
2+
3+
import com.google.gson.JsonPrimitive;
4+
import redis.clients.jedis.search.querybuilder.RangeValue;
5+
6+
import java.time.Instant;
7+
import java.time.LocalDate;
8+
import java.time.ZoneId;
9+
import java.util.Date;
10+
11+
public class LocalDateRangeValue extends RangeValue {
12+
private final LocalDate from;
13+
private final LocalDate to;
14+
15+
public LocalDateRangeValue(LocalDate from, LocalDate to) {
16+
this.from = from;
17+
this.to = to;
18+
}
19+
20+
private static void appendLocalDate(StringBuilder sb, LocalDate localDate, boolean inclusive) {
21+
if (!inclusive) {
22+
sb.append("(");
23+
}
24+
25+
if (localDate == LocalDate.MIN) {
26+
sb.append("-inf");
27+
} else if (localDate == LocalDate.MAX) {
28+
sb.append("inf");
29+
} else {
30+
Instant instant = localDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
31+
long unixTime = instant.getEpochSecond();
32+
sb.append(new JsonPrimitive(unixTime));
33+
}
34+
35+
}
36+
37+
@Override
38+
protected void appendFrom(StringBuilder sb, boolean inclusive) {
39+
appendLocalDate(sb, this.from, inclusive);
40+
}
41+
42+
@Override
43+
protected void appendTo(StringBuilder sb, boolean inclusive) {
44+
appendLocalDate(sb, this.to, inclusive);
45+
}
46+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.redis.om.spring.search.stream.predicates.jedis;
2+
3+
import com.google.gson.JsonPrimitive;
4+
import redis.clients.jedis.search.querybuilder.RangeValue;
5+
6+
import java.time.Instant;
7+
import java.time.LocalDateTime;
8+
import java.time.ZoneId;
9+
10+
public class LocalDateTimeRangeValue extends RangeValue {
11+
private final LocalDateTime from;
12+
private final LocalDateTime to;
13+
14+
public LocalDateTimeRangeValue(LocalDateTime from, LocalDateTime to) {
15+
this.from = from;
16+
this.to = to;
17+
}
18+
19+
private static void appendLocalDateTime(StringBuilder sb, LocalDateTime localDateTime, boolean inclusive) {
20+
if (!inclusive) {
21+
sb.append("(");
22+
}
23+
24+
if (localDateTime == LocalDateTime.MIN) {
25+
sb.append("-inf");
26+
} else if (localDateTime == LocalDateTime.MAX) {
27+
sb.append("inf");
28+
} else {
29+
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
30+
long timeInMillis = instant.toEpochMilli();
31+
sb.append(new JsonPrimitive(timeInMillis));
32+
}
33+
}
34+
35+
@Override
36+
protected void appendFrom(StringBuilder sb, boolean inclusive) {
37+
appendLocalDateTime(sb, this.from, inclusive);
38+
}
39+
40+
@Override
41+
protected void appendTo(StringBuilder sb, boolean inclusive) {
42+
appendLocalDateTime(sb, this.to, inclusive);
43+
}
44+
}

redis-om-spring/src/main/java/com/redis/om/spring/search/stream/predicates/numeric/BetweenPredicate.java

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.redis.om.spring.metamodel.SearchFieldAccessor;
44
import com.redis.om.spring.search.stream.predicates.BaseAbstractPredicate;
5+
import com.redis.om.spring.search.stream.predicates.jedis.JedisValues;
56
import redis.clients.jedis.search.querybuilder.Node;
67
import redis.clients.jedis.search.querybuilder.QueryBuilders;
78
import redis.clients.jedis.search.querybuilder.Values;
@@ -36,40 +37,18 @@ public T getMax() {
3637
@Override
3738
public Node apply(Node root) {
3839
boolean paramsPresent = isNotEmpty(getMin()) && isNotEmpty(getMax());
39-
if (!paramsPresent) return root;
40+
if (!paramsPresent)
41+
return root;
4042
Class<?> cls = min.getClass();
4143
if (cls == LocalDate.class) {
42-
LocalDate minLocalDate = (LocalDate) min;
43-
LocalDate maxLocalDate = (LocalDate) max;
44-
Instant minInstant = minLocalDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
45-
Instant maxInstant = maxLocalDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
46-
long minUnixTime = minInstant.getEpochSecond();
47-
long maxUnixTime = maxInstant.getEpochSecond();
48-
return QueryBuilders.intersect(root).add(getSearchAlias(),
49-
Values.between(Double.parseDouble(Long.toString(minUnixTime)), Double.parseDouble(Long.toString(maxUnixTime))));
44+
return QueryBuilders.intersect(root).add(getSearchAlias(), JedisValues.between((LocalDate) min, (LocalDate) max));
5045
} else if (cls == Date.class) {
51-
Date minLocalDate = (Date) min;
52-
Date maxLocalDate = (Date) max;
53-
Instant minInstant = minLocalDate.toInstant();
54-
Instant maxInstant = maxLocalDate.toInstant();
55-
long minUnixTime = minInstant.getEpochSecond();
56-
long maxUnixTime = maxInstant.getEpochSecond();
57-
return QueryBuilders.intersect(root).add(getSearchAlias(),
58-
Values.between(Double.parseDouble(Long.toString(minUnixTime)), Double.parseDouble(Long.toString(maxUnixTime))));
46+
return QueryBuilders.intersect(root).add(getSearchAlias(), JedisValues.between((Date) min, (Date) max));
5947
} else if (cls == LocalDateTime.class) {
60-
LocalDateTime minLocalDateTime = (LocalDateTime) min;
61-
LocalDateTime maxLocalDateTime = (LocalDateTime) max;
62-
63-
long minUnixTime = minLocalDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
64-
long maxUnixTime = maxLocalDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
65-
66-
return QueryBuilders.intersect(root).add(getSearchAlias(), Values.between(minUnixTime, maxUnixTime));
48+
return QueryBuilders.intersect(root)
49+
.add(getSearchAlias(), JedisValues.between((LocalDateTime) min, (LocalDateTime) max));
6750
} else if (cls == Instant.class) {
68-
Instant minInstant = (Instant) min;
69-
Instant maxInstant = (Instant) max;
70-
long minUnixTime = minInstant.getEpochSecond();
71-
long maxUnixTime = maxInstant.getEpochSecond();
72-
return QueryBuilders.intersect(root).add(getSearchAlias(), Values.between(minUnixTime, maxUnixTime));
51+
return QueryBuilders.intersect(root).add(getSearchAlias(), JedisValues.between((Instant) min, (Instant) max));
7352
} else if (cls == Integer.class) {
7453
return QueryBuilders.intersect(root).add(getSearchAlias(),
7554
Values.between(Integer.parseInt(getMin().toString()), Integer.parseInt(getMax().toString())));

0 commit comments

Comments
 (0)