Feature - support for gt, ge, lt and le conditions in LiveObject search engine #2324

pull/2329/head
Nikita Koksharov 6 years ago
parent 7ac980b3f5
commit 2f7b089a14

@ -21,7 +21,6 @@ import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
@ -36,6 +35,7 @@ import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
import org.redisson.api.RCascadeType;
import org.redisson.api.RDeque;
@ -50,7 +50,6 @@ import org.redisson.api.RObject;
import org.redisson.api.RObjectAsync;
import org.redisson.api.RQueue;
import org.redisson.api.RSet;
import org.redisson.api.RSetMultimap;
import org.redisson.api.RSortedSet;
import org.redisson.api.RedissonClient;
import org.redisson.api.annotation.RCascade;
@ -60,10 +59,8 @@ import org.redisson.api.annotation.RId;
import org.redisson.api.annotation.RIndex;
import org.redisson.api.condition.Condition;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.liveobject.LiveObjectSearch;
import org.redisson.liveobject.LiveObjectTemplate;
import org.redisson.liveobject.condition.ANDCondition;
import org.redisson.liveobject.condition.EQCondition;
import org.redisson.liveobject.condition.ORCondition;
import org.redisson.liveobject.core.AccessorInterceptor;
import org.redisson.liveobject.core.FieldAccessorInterceptor;
import org.redisson.liveobject.core.LiveObjectInterceptor;
@ -72,7 +69,6 @@ import org.redisson.liveobject.core.RMapInterceptor;
import org.redisson.liveobject.misc.AdvBeanCopy;
import org.redisson.liveobject.misc.ClassUtils;
import org.redisson.liveobject.misc.Introspectior;
import org.redisson.liveobject.resolver.NamingScheme;
import org.redisson.liveobject.resolver.RIdResolver;
import jodd.bean.BeanCopy;
@ -93,11 +89,13 @@ public class RedissonLiveObjectService implements RLiveObjectService {
private final ConcurrentMap<Class<?>, Class<?>> classCache;
private final RedissonClient redisson;
private final CommandAsyncExecutor commandExecutor;
private final LiveObjectSearch seachEngine;
public RedissonLiveObjectService(RedissonClient redisson, ConcurrentMap<Class<?>, Class<?>> classCache, CommandAsyncExecutor commandExecutor) {
this.redisson = redisson;
this.classCache = classCache;
this.commandExecutor = commandExecutor;
this.seachEngine = new LiveObjectSearch(redisson, commandExecutor.getObjectBuilder());
}
//TODO: Add ttl renewal functionality
@ -148,110 +146,13 @@ public class RedissonLiveObjectService implements RLiveObjectService {
return null;
}
Set<Object> traverseAnd(ANDCondition condition, NamingScheme namingScheme, Class<?> entityClass) {
Set<Object> allIds = new HashSet<Object>();
RSet<Object> firstSet = null;
List<String> names = new ArrayList<String>();
boolean isAllEqConditions = true;
for (Condition cond : condition.getConditions()) {
if (cond instanceof EQCondition) {
EQCondition eqc = (EQCondition) cond;
String indexName = namingScheme.getIndexName(entityClass, eqc.getName());
RSetMultimap<Object, Object> map = redisson.getSetMultimap(indexName, namingScheme.getCodec());
RSet<Object> values = map.get(eqc.getValue());
if (firstSet == null) {
firstSet = values;
} else {
names.add(values.getName());
}
}
if (cond instanceof ORCondition) {
isAllEqConditions = false;
Collection<Object> ids = traverseOr((ORCondition) cond, namingScheme, entityClass);
allIds.addAll(ids);
}
}
if (!isAllEqConditions && allIds.isEmpty()) {
return Collections.emptySet();
}
if (firstSet != null) {
if (names.isEmpty()) {
if (!isAllEqConditions && !allIds.isEmpty()) {
allIds.retainAll(firstSet.readAll());
} else {
allIds.addAll(firstSet.readAll());
}
} else {
Set<Object> intersect = firstSet.readIntersection(names.toArray(new String[names.size()]));
if (!isAllEqConditions && !allIds.isEmpty()) {
allIds.retainAll(intersect);
} else {
allIds.addAll(intersect);
}
}
}
return allIds;
}
Set<Object> traverseOr(ORCondition condition, NamingScheme namingScheme, Class<?> entityClass) {
Set<Object> allIds = new HashSet<Object>();
RSet<Object> firstSet = null;
List<String> names = new ArrayList<String>();
for (Condition cond : condition.getConditions()) {
if (cond instanceof EQCondition) {
EQCondition eqc = (EQCondition) cond;
String indexName = namingScheme.getIndexName(entityClass, eqc.getName());
RSetMultimap<Object, Object> map = redisson.getSetMultimap(indexName, namingScheme.getCodec());
RSet<Object> values = map.get(eqc.getValue());
if (firstSet == null) {
firstSet = values;
} else {
names.add(values.getName());
}
}
if (cond instanceof ANDCondition) {
Collection<Object> ids = traverseAnd((ANDCondition) cond, namingScheme, entityClass);
allIds.addAll(ids);
}
}
if (firstSet != null) {
if (names.isEmpty()) {
allIds.addAll(firstSet.readAll());
} else {
allIds.addAll(firstSet.readUnion(names.toArray(new String[names.size()])));
}
}
return allIds;
}
@Override
public <T> Collection<T> find(Class<T> entityClass, Condition condition) {
NamingScheme namingScheme = commandExecutor.getObjectBuilder().getNamingScheme(entityClass);
Set<Object> ids = Collections.emptySet();
if (condition instanceof EQCondition) {
EQCondition c = (EQCondition) condition;
String indexName = namingScheme.getIndexName(entityClass, c.getName());
RSetMultimap<Object, Object> map = redisson.getSetMultimap(indexName, namingScheme.getCodec());
ids = map.getAll(c.getValue());
} else if (condition instanceof ORCondition) {
ids = traverseOr((ORCondition) condition, namingScheme, entityClass);
} else if (condition instanceof ANDCondition) {
ids = traverseAnd((ANDCondition) condition, namingScheme, entityClass);
}
Set<Object> ids = seachEngine.find(entityClass, condition);
if (ids.isEmpty()) {
return Collections.emptyList();
}
List<T> result = new ArrayList<T>(ids.size());
for (Object id : ids) {
T proxied = createLiveObject(entityClass, id);
result.add(proxied);
}
return result;
return ids.stream()
.map(id -> createLiveObject(entityClass, id))
.collect(Collectors.toList());
}
@Override

@ -21,6 +21,10 @@ import java.util.List;
import org.redisson.liveobject.condition.ANDCondition;
import org.redisson.liveobject.condition.EQCondition;
import org.redisson.liveobject.condition.GECondition;
import org.redisson.liveobject.condition.GTCondition;
import org.redisson.liveobject.condition.LECondition;
import org.redisson.liveobject.condition.LTCondition;
import org.redisson.liveobject.condition.ORCondition;
/**
@ -92,6 +96,50 @@ public final class Conditions {
return new ANDCondition(conditions);
}
/**
* Returns "GREATER THAN" condition which restricts property by <code>name</code> to defined <code>value</code>
*
* @param name - name of property
* @param value - defined value
* @return condition
*/
public static Condition gt(String name, Number value) {
return new GTCondition(name, value);
}
/**
* Returns "LESS THAN" condition which restricts property by <code>name</code> to defined <code>value</code>
*
* @param name - name of property
* @param value - defined value
* @return condition
*/
public static Condition lt(String name, Number value) {
return new LTCondition(name, value);
}
/**
* Returns "GREATER THAN ON EQUAL" condition which restricts property by <code>name</code> to defined <code>value</code>
*
* @param name - name of property
* @param value - defined value
* @return condition
*/
public static Condition ge(String name, Number value) {
return new GECondition(name, value);
}
/**
* Returns "LESS THAN ON EQUAL" condition which restricts property by <code>name</code> to defined <code>value</code>
*
* @param name - name of property
* @param value - defined value
* @return condition
*/
public static Condition le(String name, Number value) {
return new LECondition(name, value);
}
private Conditions() {
}

@ -0,0 +1,358 @@
/**
* Copyright (c) 2013-2019 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.liveobject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.BiFunction;
import org.redisson.api.RScoredSortedSet;
import org.redisson.api.RSet;
import org.redisson.api.RSetMultimap;
import org.redisson.api.RedissonClient;
import org.redisson.api.condition.Condition;
import org.redisson.liveobject.condition.ANDCondition;
import org.redisson.liveobject.condition.EQCondition;
import org.redisson.liveobject.condition.GECondition;
import org.redisson.liveobject.condition.GTCondition;
import org.redisson.liveobject.condition.LECondition;
import org.redisson.liveobject.condition.LTCondition;
import org.redisson.liveobject.condition.ORCondition;
import org.redisson.liveobject.core.RedissonObjectBuilder;
import org.redisson.liveobject.resolver.NamingScheme;
/**
*
* @author Nikita Koksharov
*
*/
public class LiveObjectSearch {
private final RedissonClient redisson;
private final RedissonObjectBuilder objectBuilder;
public LiveObjectSearch(RedissonClient redisson, RedissonObjectBuilder objectBuilder) {
super();
this.redisson = redisson;
this.objectBuilder = objectBuilder;
}
private Set<Object> traverseAnd(ANDCondition condition, NamingScheme namingScheme, Class<?> entityClass) {
Set<Object> allIds = new HashSet<Object>();
RSet<Object> firstEqSet = null;
List<String> eqNames = new ArrayList<String>();
Map<RScoredSortedSet<Object>, Number> gtNumericNames = new HashMap<>();
Map<RScoredSortedSet<Object>, Number> geNumericNames = new HashMap<>();
Map<RScoredSortedSet<Object>, Number> ltNumericNames = new HashMap<>();
Map<RScoredSortedSet<Object>, Number> leNumericNames = new HashMap<>();
Map<RScoredSortedSet<Object>, Number> eqNumericNames = new HashMap<>();
for (Condition cond : condition.getConditions()) {
if (cond instanceof EQCondition) {
EQCondition eqc = (EQCondition) cond;
String indexName = namingScheme.getIndexName(entityClass, eqc.getName());
if (eqc.getValue() instanceof Number) {
RScoredSortedSet<Object> values = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
eqNumericNames.put(values, (Number) eqc.getValue());
} else {
RSetMultimap<Object, Object> map = redisson.getSetMultimap(indexName, namingScheme.getCodec());
RSet<Object> values = map.get(eqc.getValue());
if (firstEqSet == null) {
firstEqSet = values;
} else {
eqNames.add(values.getName());
}
}
}
if (cond instanceof LTCondition) {
LTCondition ltc = (LTCondition) cond;
String indexName = namingScheme.getIndexName(entityClass, ltc.getName());
RScoredSortedSet<Object> values = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
ltNumericNames.put(values, ltc.getValue());
}
if (cond instanceof LECondition) {
LECondition lec = (LECondition) cond;
String indexName = namingScheme.getIndexName(entityClass, lec.getName());
RScoredSortedSet<Object> values = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
leNumericNames.put(values, lec.getValue());
}
if (cond instanceof GECondition) {
GECondition gec = (GECondition) cond;
String indexName = namingScheme.getIndexName(entityClass, gec.getName());
RScoredSortedSet<Object> values = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
geNumericNames.put(values, gec.getValue());
}
if (cond instanceof GTCondition) {
GTCondition gtc = (GTCondition) cond;
String indexName = namingScheme.getIndexName(entityClass, gtc.getName());
RScoredSortedSet<Object> values = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
gtNumericNames.put(values, gtc.getValue());
}
if (cond instanceof ORCondition) {
Collection<Object> ids = traverseOr((ORCondition) cond, namingScheme, entityClass);
if (ids.isEmpty()) {
return Collections.emptySet();
}
allIds.addAll(ids);
}
}
if (firstEqSet != null) {
if (eqNames.isEmpty()) {
if (!allIds.isEmpty()) {
allIds.retainAll(firstEqSet.readAll());
} else {
allIds.addAll(firstEqSet.readAll());
}
} else {
Set<Object> intersect = firstEqSet.readIntersection(eqNames.toArray(new String[eqNames.size()]));
if (!allIds.isEmpty()) {
allIds.retainAll(intersect);
} else {
allIds.addAll(intersect);
}
}
}
if (!checkValueRange(allIds, eqNumericNames, (r, v) -> {
return r.valueRange(v.doubleValue(), true, v.doubleValue(), true);
})) {
return Collections.emptySet();
}
if (!checkValueRange(allIds, gtNumericNames, (r, v) -> {
return r.valueRange(v.doubleValue(), false, Double.POSITIVE_INFINITY, false);
})) {
return Collections.emptySet();
}
if (!checkValueRange(allIds, geNumericNames, (r, v) -> {
return r.valueRange(v.doubleValue(), true, Double.POSITIVE_INFINITY, false);
})) {
return Collections.emptySet();
}
if (!checkValueRange(allIds, ltNumericNames, (r, v) -> {
return r.valueRange(Double.NEGATIVE_INFINITY, false, v.doubleValue(), false);
})) {
return Collections.emptySet();
}
if (!checkValueRange(allIds, leNumericNames, (r, v) -> {
return r.valueRange(Double.NEGATIVE_INFINITY, false, v.doubleValue(), true);
})) {
return Collections.emptySet();
}
return allIds;
}
private boolean checkValueRange(Set<Object> allIds, Map<RScoredSortedSet<Object>, Number> numericNames,
BiFunction<RScoredSortedSet<Object>, Number, Collection<Object>> func) {
if (!numericNames.isEmpty()) {
Set<Object> gtAllIds = new HashSet<>();
boolean firstFill = false;
for (Entry<RScoredSortedSet<Object>, Number> e : numericNames.entrySet()) {
Collection<Object> gtIds = func.apply(e.getKey(), e.getValue());
if (gtIds.isEmpty()) {
return false;
}
if (!firstFill) {
gtAllIds.addAll(gtIds);
firstFill = true;
} else {
gtAllIds.retainAll(gtIds);
}
}
if (!allIds.isEmpty()) {
allIds.retainAll(gtAllIds);
} else {
allIds.addAll(gtAllIds);
}
}
return true;
}
private Set<Object> traverseOr(ORCondition condition, NamingScheme namingScheme, Class<?> entityClass) {
Set<Object> allIds = new HashSet<Object>();
RSet<Object> firstEqSet = null;
List<String> eqNames = new ArrayList<String>();
Map<RScoredSortedSet<Object>, Number> ltNumericNames = new HashMap<>();
Map<RScoredSortedSet<Object>, Number> leNumericNames = new HashMap<>();
Map<RScoredSortedSet<Object>, Number> gtNumericNames = new HashMap<>();
Map<RScoredSortedSet<Object>, Number> geNumericNames = new HashMap<>();
Map<RScoredSortedSet<Object>, Number> eqNumericNames = new HashMap<>();
for (Condition cond : condition.getConditions()) {
if (cond instanceof EQCondition) {
EQCondition eqc = (EQCondition) cond;
String indexName = namingScheme.getIndexName(entityClass, eqc.getName());
if (eqc.getValue() instanceof Number) {
RScoredSortedSet<Object> values = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
eqNumericNames.put(values, (Number) eqc.getValue());
} else {
RSetMultimap<Object, Object> map = redisson.getSetMultimap(indexName, namingScheme.getCodec());
RSet<Object> values = map.get(eqc.getValue());
if (firstEqSet == null) {
firstEqSet = values;
} else {
eqNames.add(values.getName());
}
}
}
if (cond instanceof GTCondition) {
GTCondition gtc = (GTCondition) cond;
String indexName = namingScheme.getIndexName(entityClass, gtc.getName());
RScoredSortedSet<Object> values = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
gtNumericNames.put(values, gtc.getValue());
}
if (cond instanceof GECondition) {
GECondition gec = (GECondition) cond;
String indexName = namingScheme.getIndexName(entityClass, gec.getName());
RScoredSortedSet<Object> values = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
geNumericNames.put(values, gec.getValue());
}
if (cond instanceof LTCondition) {
LTCondition ltc = (LTCondition) cond;
String indexName = namingScheme.getIndexName(entityClass, ltc.getName());
RScoredSortedSet<Object> values = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
ltNumericNames.put(values, ltc.getValue());
}
if (cond instanceof LECondition) {
LECondition lec = (LECondition) cond;
String indexName = namingScheme.getIndexName(entityClass, lec.getName());
RScoredSortedSet<Object> values = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
leNumericNames.put(values, lec.getValue());
}
if (cond instanceof ANDCondition) {
Collection<Object> ids = traverseAnd((ANDCondition) cond, namingScheme, entityClass);
allIds.addAll(ids);
}
}
if (firstEqSet != null) {
if (eqNames.isEmpty()) {
allIds.addAll(firstEqSet.readAll());
} else {
allIds.addAll(firstEqSet.readUnion(eqNames.toArray(new String[eqNames.size()])));
}
}
for (Entry<RScoredSortedSet<Object>, Number> e : eqNumericNames.entrySet()) {
Collection<Object> ids = e.getKey().valueRange(e.getValue().doubleValue(), true, e.getValue().doubleValue(), true);
allIds.addAll(ids);
}
for (Entry<RScoredSortedSet<Object>, Number> e : gtNumericNames.entrySet()) {
Collection<Object> ids = e.getKey().valueRange(e.getValue().doubleValue(), false, Double.POSITIVE_INFINITY, false);
allIds.addAll(ids);
}
for (Entry<RScoredSortedSet<Object>, Number> e : geNumericNames.entrySet()) {
Collection<Object> ids = e.getKey().valueRange(e.getValue().doubleValue(), true, Double.POSITIVE_INFINITY, false);
allIds.addAll(ids);
}
for (Entry<RScoredSortedSet<Object>, Number> e : ltNumericNames.entrySet()) {
Collection<Object> ids = e.getKey().valueRange(Double.NEGATIVE_INFINITY, false, e.getValue().doubleValue(), false);
allIds.addAll(ids);
}
for (Entry<RScoredSortedSet<Object>, Number> e : leNumericNames.entrySet()) {
Collection<Object> ids = e.getKey().valueRange(Double.NEGATIVE_INFINITY, false, e.getValue().doubleValue(), true);
allIds.addAll(ids);
}
return allIds;
}
public Set<Object> find(Class<?> entityClass, Condition condition) {
NamingScheme namingScheme = objectBuilder.getNamingScheme(entityClass);
Set<Object> ids = Collections.emptySet();
if (condition instanceof EQCondition) {
EQCondition c = (EQCondition) condition;
String indexName = namingScheme.getIndexName(entityClass, c.getName());
if (c.getValue() instanceof Number) {
RScoredSortedSet<Object> set = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
double v = ((Number) c.getValue()).doubleValue();
Collection<Object> gtIds = set.valueRange(v, true, v, true);
ids = new HashSet<>(gtIds);
} else {
RSetMultimap<Object, Object> map = redisson.getSetMultimap(indexName, namingScheme.getCodec());
ids = map.getAll(c.getValue());
}
} else if (condition instanceof GTCondition) {
GTCondition c = (GTCondition) condition;
String indexName = namingScheme.getIndexName(entityClass, c.getName());
RScoredSortedSet<Object> set = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
Collection<Object> gtIds = set.valueRange(c.getValue().doubleValue(), false, Double.POSITIVE_INFINITY, false);
ids = new HashSet<>(gtIds);
} else if (condition instanceof GECondition) {
GECondition c = (GECondition) condition;
String indexName = namingScheme.getIndexName(entityClass, c.getName());
RScoredSortedSet<Object> set = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
Collection<Object> gtIds = set.valueRange(c.getValue().doubleValue(), true, Double.POSITIVE_INFINITY, false);
ids = new HashSet<>(gtIds);
} else if (condition instanceof LTCondition) {
LTCondition c = (LTCondition) condition;
String indexName = namingScheme.getIndexName(entityClass, c.getName());
RScoredSortedSet<Object> set = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
Collection<Object> gtIds = set.valueRange(Double.NEGATIVE_INFINITY, false, c.getValue().doubleValue(), false);
ids = new HashSet<>(gtIds);
} else if (condition instanceof LECondition) {
LECondition c = (LECondition) condition;
String indexName = namingScheme.getIndexName(entityClass, c.getName());
RScoredSortedSet<Object> set = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
Collection<Object> gtIds = set.valueRange(Double.NEGATIVE_INFINITY, false, c.getValue().doubleValue(), true);
ids = new HashSet<>(gtIds);
} else if (condition instanceof ORCondition) {
ids = traverseOr((ORCondition) condition, namingScheme, entityClass);
} else if (condition instanceof ANDCondition) {
ids = traverseAnd((ANDCondition) condition, namingScheme, entityClass);
}
if (ids.isEmpty()) {
return Collections.emptySet();
}
return ids;
}
}

@ -0,0 +1,43 @@
/**
* Copyright (c) 2013-2019 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.liveobject.condition;
import org.redisson.api.condition.Condition;
/**
*
* @author Nikita Koksharov
*
*/
public class GECondition implements Condition {
private final String name;
private final Number value;
public GECondition(String name, Number value) {
super();
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public Number getValue() {
return value;
}
}

@ -0,0 +1,43 @@
/**
* Copyright (c) 2013-2019 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.liveobject.condition;
import org.redisson.api.condition.Condition;
/**
*
* @author Nikita Koksharov
*
*/
public class GTCondition implements Condition {
private final String name;
private final Number value;
public GTCondition(String name, Number value) {
super();
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public Number getValue() {
return value;
}
}

@ -0,0 +1,43 @@
/**
* Copyright (c) 2013-2019 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.liveobject.condition;
import org.redisson.api.condition.Condition;
/**
*
* @author Nikita Koksharov
*
*/
public class LECondition implements Condition {
private final String name;
private final Number value;
public LECondition(String name, Number value) {
super();
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public Number getValue() {
return value;
}
}

@ -0,0 +1,43 @@
/**
* Copyright (c) 2013-2019 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.liveobject.condition;
import org.redisson.api.condition.Condition;
/**
*
* @author Nikita Koksharov
*
*/
public class LTCondition implements Condition {
private final String name;
private final Number value;
public LTCondition(String name, Number value) {
super();
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public Number getValue() {
return value;
}
}

@ -25,6 +25,7 @@ import org.redisson.RedissonReference;
import org.redisson.api.RLiveObject;
import org.redisson.api.RMap;
import org.redisson.api.RObject;
import org.redisson.api.RScoredSortedSet;
import org.redisson.api.RSetMultimap;
import org.redisson.api.RedissonClient;
import org.redisson.api.annotation.REntity;
@ -61,6 +62,7 @@ public class AccessorInterceptor {
}
@RuntimeType
@SuppressWarnings("NestedIfDepth")
public Object intercept(@Origin Method method, @SuperCall Callable<?> superMethod,
@AllArguments Object[] args, @This Object me,
@FieldValue("liveObjectLiveMap") RMap<String, Object> liveMap) throws Exception {
@ -148,11 +150,17 @@ public class AccessorInterceptor {
if (field.getAnnotation(RIndex.class) != null) {
NamingScheme namingScheme = objectBuilder.getNamingScheme(me.getClass().getSuperclass());
String indexName = namingScheme.getIndexName(me.getClass().getSuperclass(), fieldName);
RSetMultimap<Object, Object> map = redisson.getSetMultimap(indexName, namingScheme.getCodec());
if (oldArg instanceof RLiveObject) {
map.remove(((RLiveObject) oldArg).getLiveObjectId(), ((RLiveObject) me).getLiveObjectId());
if (oldArg instanceof Number) {
RScoredSortedSet<Object> set = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
set.remove(((RLiveObject) me).getLiveObjectId());
} else {
map.remove(oldArg, ((RLiveObject) me).getLiveObjectId());
RSetMultimap<Object, Object> map = redisson.getSetMultimap(indexName, namingScheme.getCodec());
if (oldArg instanceof RLiveObject) {
map.remove(((RLiveObject) oldArg).getLiveObjectId(), ((RLiveObject) me).getLiveObjectId());
} else {
map.remove(oldArg, ((RLiveObject) me).getLiveObjectId());
}
}
}
} else {
@ -169,8 +177,13 @@ public class AccessorInterceptor {
if (field.getAnnotation(RIndex.class) != null) {
NamingScheme namingScheme = objectBuilder.getNamingScheme(me.getClass().getSuperclass());
String indexName = namingScheme.getIndexName(me.getClass().getSuperclass(), field.getName());
RSetMultimap<Object, Object> map = redisson.getSetMultimap(indexName, namingScheme.getCodec());
map.put(arg, ((RLiveObject) me).getLiveObjectId());
if (arg instanceof Number) {
RScoredSortedSet<Object> set = redisson.getScoredSortedSet(indexName, namingScheme.getCodec());
set.add(((Number) arg).doubleValue(), ((RLiveObject) me).getLiveObjectId());
} else {
RSetMultimap<Object, Object> map = redisson.getSetMultimap(indexName, namingScheme.getCodec());
map.put(arg, ((RLiveObject) me).getLiveObjectId());
}
}
}

@ -22,6 +22,7 @@ import org.redisson.api.RFuture;
import org.redisson.api.RLiveObject;
import org.redisson.api.RMap;
import org.redisson.api.RMultimapAsync;
import org.redisson.api.RScoredSortedSetAsync;
import org.redisson.api.RedissonClient;
import org.redisson.api.annotation.RIndex;
import org.redisson.client.RedisException;
@ -127,10 +128,17 @@ public class LiveObjectInterceptor {
for (InDefinedShape field : fields) {
String fieldName = field.getName();
Object value = map.get(fieldName);
NamingScheme namingScheme = objectBuilder.getNamingScheme(me.getClass().getSuperclass());
String indexName = namingScheme.getIndexName(me.getClass().getSuperclass(), fieldName);
RMultimapAsync<Object, Object> idsMultimap = batch.getSetMultimap(indexName, namingScheme.getCodec());
idsMultimap.removeAsync(value, ((RLiveObject) me).getLiveObjectId());
if (value instanceof Number) {
RScoredSortedSetAsync<Object> set = batch.getScoredSortedSet(indexName, namingScheme.getCodec());
set.removeAsync(((RLiveObject) me).getLiveObjectId());
} else {
RMultimapAsync<Object, Object> idsMultimap = batch.getSetMultimap(indexName, namingScheme.getCodec());
idsMultimap.removeAsync(value, ((RLiveObject) me).getLiveObjectId());
}
}
RFuture<Long> deleteFuture = batch.getKeys().deleteAsync(map.getName());
batch.execute();

@ -14,6 +14,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@ -370,9 +371,223 @@ public class RedissonLiveObjectServiceTest extends BaseTest {
}
}
@Test
public void testFindLe() {
RLiveObjectService s = redisson.getLiveObjectService();
TestIndexed t1 = new TestIndexed("1");
t1.setNum1(12);
t1 = s.persist(t1);
TestIndexed t2 = new TestIndexed("2");
t2.setNum1(10);
t2 = s.persist(t2);
Collection<TestIndexed> objects2 = s.find(TestIndexed.class, Conditions.le("num1", 9));
assertThat(objects2).isEmpty();
Collection<TestIndexed> objects0 = s.find(TestIndexed.class, Conditions.le("num1", 12));
assertThat(objects0).hasSize(2);
Iterator<TestIndexed> iter = objects0.iterator();
TestIndexed obj1 = iter.next();
assertThat(obj1.getId()).isEqualTo(t1.getId());
TestIndexed obj2 = iter.next();
assertThat(obj2.getId()).isEqualTo(t2.getId());
s.delete(t1);
s.delete(t2);
Collection<TestIndexed> objects3 = s.find(TestIndexed.class, Conditions.le("num1", 12));
assertThat(objects3).isEmpty();
TestIndexed t3 = new TestIndexed("3");
t3.setName1("test31");
t3.setNum1(32);
t3.setBool1(false);
t3 = s.persist(t3);
TestIndexed t4 = new TestIndexed("4");
t4 = s.persist(t4);
t4.setName1("test41");
t4.setNum1(42);
t4.setBool1(true);
Collection<TestIndexed> objects4 = s.find(TestIndexed.class, Conditions.or(Conditions.le("num1", 30), Conditions.le("num1", 32)));
assertThat(objects4).hasSize(1);
Collection<TestIndexed> objects41 = s.find(TestIndexed.class, Conditions.or(Conditions.le("num1", 31), Conditions.lt("num1", -1)));
assertThat(objects41).hasSize(0);
Collection<TestIndexed> objects5 = s.find(TestIndexed.class, Conditions.or(Conditions.and(Conditions.eq("name1", "test31"), Conditions.le("num1", 32)),
Conditions.and(Conditions.eq("name1", "test41"), Conditions.le("num1", 42))));
assertThat(objects5).hasSize(2);
Collection<TestIndexed> objects6 = s.find(TestIndexed.class, Conditions.or(Conditions.eq("name1", "test34"),
Conditions.and(Conditions.eq("name1", "test41"), Conditions.le("num1", 42))));
assertThat(objects6.iterator().next().getId()).isEqualTo("4");
}
@Test
public void testFindLt() {
RLiveObjectService s = redisson.getLiveObjectService();
TestIndexed t1 = new TestIndexed("1");
t1.setNum1(12);
t1 = s.persist(t1);
TestIndexed t2 = new TestIndexed("2");
t2.setNum1(10);
t2 = s.persist(t2);
Collection<TestIndexed> objects2 = s.find(TestIndexed.class, Conditions.lt("num1", 9));
assertThat(objects2).isEmpty();
Collection<TestIndexed> objects0 = s.find(TestIndexed.class, Conditions.lt("num1", 13));
assertThat(objects0).hasSize(2);
Iterator<TestIndexed> iter = objects0.iterator();
TestIndexed obj1 = iter.next();
assertThat(obj1.getId()).isEqualTo(t1.getId());
TestIndexed obj2 = iter.next();
assertThat(obj2.getId()).isEqualTo(t2.getId());
s.delete(t1);
s.delete(t2);
Collection<TestIndexed> objects3 = s.find(TestIndexed.class, Conditions.lt("num1", 13));
assertThat(objects3).isEmpty();
TestIndexed t3 = new TestIndexed("3");
t3.setName1("test31");
t3.setNum1(32);
t3.setBool1(false);
t3 = s.persist(t3);
TestIndexed t4 = new TestIndexed("4");
t4 = s.persist(t4);
t4.setName1("test41");
t4.setNum1(42);
t4.setBool1(true);
Collection<TestIndexed> objects4 = s.find(TestIndexed.class, Conditions.or(Conditions.lt("num1", 30), Conditions.lt("num1", 33)));
assertThat(objects4).hasSize(1);
Collection<TestIndexed> objects41 = s.find(TestIndexed.class, Conditions.or(Conditions.lt("num1", 32), Conditions.lt("num1", -1)));
assertThat(objects41).hasSize(0);
Collection<TestIndexed> objects5 = s.find(TestIndexed.class, Conditions.or(Conditions.and(Conditions.eq("name1", "test31"), Conditions.lt("num1", 33)),
Conditions.and(Conditions.eq("name1", "test41"), Conditions.lt("num1", 43))));
assertThat(objects5).hasSize(2);
Collection<TestIndexed> objects6 = s.find(TestIndexed.class, Conditions.or(Conditions.eq("name1", "test34"),
Conditions.and(Conditions.eq("name1", "test41"), Conditions.lt("num1", 43))));
assertThat(objects6.iterator().next().getId()).isEqualTo("4");
}
@Test
public void testFindGe() {
RLiveObjectService s = redisson.getLiveObjectService();
TestIndexed t1 = new TestIndexed("1");
t1.setNum1(12);
t1 = s.persist(t1);
TestIndexed t2 = new TestIndexed("2");
t2.setNum1(10);
t2 = s.persist(t2);
Collection<TestIndexed> objects0 = s.find(TestIndexed.class, Conditions.ge("num1", 10));
assertThat(objects0).hasSize(2);
Iterator<TestIndexed> iter = objects0.iterator();
TestIndexed obj1 = iter.next();
assertThat(obj1.getId()).isEqualTo(t1.getId());
TestIndexed obj2 = iter.next();
assertThat(obj2.getId()).isEqualTo(t2.getId());
s.delete(t1);
s.delete(t2);
Collection<TestIndexed> objects3 = s.find(TestIndexed.class, Conditions.ge("num1", 10));
assertThat(objects3).isEmpty();
TestIndexed t3 = new TestIndexed("3");
t3.setName1("test31");
t3.setNum1(32);
t3.setBool1(false);
t3 = s.persist(t3);
TestIndexed t4 = new TestIndexed("4");
t4 = s.persist(t4);
t4.setName1("test41");
t4.setNum1(42);
t4.setBool1(true);
Collection<TestIndexed> objects4 = s.find(TestIndexed.class, Conditions.or(Conditions.ge("num1", 42), Conditions.ge("num1", 43)));
assertThat(objects4).hasSize(1);
Collection<TestIndexed> objects41 = s.find(TestIndexed.class, Conditions.or(Conditions.ge("num1", 43), Conditions.ge("num1", 45)));
assertThat(objects41).hasSize(0);
Collection<TestIndexed> objects5 = s.find(TestIndexed.class, Conditions.or(Conditions.and(Conditions.eq("name1", "test31"), Conditions.ge("num1", 32)),
Conditions.and(Conditions.eq("name1", "test41"), Conditions.ge("num1", 42))));
assertThat(objects5).hasSize(2);
Collection<TestIndexed> objects6 = s.find(TestIndexed.class, Conditions.or(Conditions.eq("name1", "test34"),
Conditions.and(Conditions.eq("name1", "test41"), Conditions.ge("num1", 42))));
assertThat(objects6.iterator().next().getId()).isEqualTo("4");
}
@Test
public void testFindGt() {
RLiveObjectService s = redisson.getLiveObjectService();
TestIndexed t1 = new TestIndexed("1");
t1.setNum1(12);
t1 = s.persist(t1);
TestIndexed t2 = new TestIndexed("2");
t2.setNum1(10);
t2 = s.persist(t2);
Collection<TestIndexed> objects0 = s.find(TestIndexed.class, Conditions.gt("num1", 9));
assertThat(objects0).hasSize(2);
Iterator<TestIndexed> iter = objects0.iterator();
TestIndexed obj1 = iter.next();
assertThat(obj1.getId()).isEqualTo(t1.getId());
TestIndexed obj2 = iter.next();
assertThat(obj2.getId()).isEqualTo(t2.getId());
s.delete(t1);
s.delete(t2);
Collection<TestIndexed> objects3 = s.find(TestIndexed.class, Conditions.gt("num1", 9));
assertThat(objects3).isEmpty();
TestIndexed t3 = new TestIndexed("3");
t3.setName1("test31");
t3.setNum1(32);
t3.setBool1(false);
t3 = s.persist(t3);
TestIndexed t4 = new TestIndexed("4");
t4 = s.persist(t4);
t4.setName1("test41");
t4.setNum1(42);
t4.setBool1(true);
Collection<TestIndexed> objects4 = s.find(TestIndexed.class, Conditions.or(Conditions.gt("num1", 40), Conditions.gt("num1", 42)));
assertThat(objects4).hasSize(1);
Collection<TestIndexed> objects41 = s.find(TestIndexed.class, Conditions.or(Conditions.gt("num1", 42), Conditions.gt("num1", 45)));
assertThat(objects41).hasSize(0);
Collection<TestIndexed> objects5 = s.find(TestIndexed.class, Conditions.or(Conditions.and(Conditions.eq("name1", "test31"), Conditions.gt("num1", 30)),
Conditions.and(Conditions.eq("name1", "test41"), Conditions.gt("num1", 40))));
assertThat(objects5).hasSize(2);
Collection<TestIndexed> objects6 = s.find(TestIndexed.class, Conditions.or(Conditions.eq("name1", "test34"),
Conditions.and(Conditions.eq("name1", "test41"), Conditions.gt("num1", 41))));
assertThat(objects6.iterator().next().getId()).isEqualTo("4");
}
@Test
public void testFind() {
public void testFindEq() {
RLiveObjectService s = redisson.getLiveObjectService();
TestIndexed t1 = new TestIndexed("1");
t1.setName1("test1");

Loading…
Cancel
Save