Skip to content

Commit 479358a

Browse files
Find By Example With Array (#197)
Co-authored-by: Paulo Ferreira <paulo.ferreira25@gmail.com>
1 parent 7502442 commit 479358a

File tree

8 files changed

+294
-197
lines changed

8 files changed

+294
-197
lines changed

src/main/java/com/arangodb/springframework/config/ArangoConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public interface ArangoConfiguration {
5656

5757
@Bean
5858
default ArangoOperations arangoTemplate() throws Exception {
59-
return new ArangoTemplate(arango().build(), database(), arangoConverter());
59+
return new ArangoTemplate(arango().build(), database(), arangoConverter(), resolverFactory());
6060
}
6161

6262
@Bean

src/main/java/com/arangodb/springframework/core/ArangoOperations.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import com.arangodb.model.DocumentReplaceOptions;
4040
import com.arangodb.model.DocumentUpdateOptions;
4141
import com.arangodb.springframework.core.convert.ArangoConverter;
42+
import com.arangodb.springframework.core.convert.resolver.ResolverFactory;
4243

4344
/**
4445
* Interface that specifies a basic set of ArangoDB operations.
@@ -581,5 +582,7 @@ public enum UpsertStrategy {
581582
Iterable<UserEntity> getUsers() throws DataAccessException;
582583

583584
ArangoConverter getConverter();
585+
586+
ResolverFactory getResolverFactory();
584587

585588
}

src/main/java/com/arangodb/springframework/core/convert/resolver/RefResolver.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ public Object resolve(final String id, final TypeInformation<?> type, final Ref
6262
}
6363

6464
@Override
65-
public String write(final Object source, final ArangoPersistentEntity<?> entity, final String id, final Ref annotation) {
66-
return MetadataUtils.createIdFromCollectionAndKey(entity.getCollection(), id);
65+
public String write(final Object source, final ArangoPersistentEntity<?> entity, final Object id, final Ref annotation) {
66+
return MetadataUtils.createIdFromCollectionAndKey(entity.getCollection(), String.valueOf(id));
6767
}
6868

6969

src/main/java/com/arangodb/springframework/core/convert/resolver/ReferenceResolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@ public interface ReferenceResolver<A extends Annotation> {
3737

3838
Object resolveMultiple(Collection<String> ids, TypeInformation<?> type, A annotation);
3939

40-
public String write(Object source, ArangoPersistentEntity<?> entity, String id, Ref annotation);
40+
public String write(Object source, ArangoPersistentEntity<?> entity, Object id, Ref annotation);
4141

4242
}

src/main/java/com/arangodb/springframework/core/template/ArangoTemplate.java

Lines changed: 113 additions & 100 deletions
Large diffs are not rendered by default.

src/main/java/com/arangodb/springframework/repository/ArangoExampleConverter.java

Lines changed: 59 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
package com.arangodb.springframework.repository;
2222

23+
import java.lang.annotation.Annotation;
2324
import java.util.HashSet;
2425
import java.util.Map;
2526
import java.util.Optional;
@@ -31,37 +32,39 @@
3132
import org.springframework.data.mapping.PersistentPropertyAccessor;
3233
import org.springframework.util.Assert;
3334

35+
import com.arangodb.springframework.core.convert.resolver.ReferenceResolver;
36+
import com.arangodb.springframework.core.convert.resolver.ResolverFactory;
3437
import com.arangodb.springframework.core.mapping.ArangoMappingContext;
3538
import com.arangodb.springframework.core.mapping.ArangoPersistentEntity;
3639
import com.arangodb.springframework.core.mapping.ArangoPersistentProperty;
3740

3841
/**
39-
* Converts Example to String representing predicate expression and puts necessary bindings in the given bindVars Map
42+
* Converts Example to String representing predicate expression and puts
43+
* necessary bindings in the given bindVars Map
4044
*/
4145
public class ArangoExampleConverter<T> {
4246

4347
private final ArangoMappingContext context;
48+
private final ResolverFactory resolverFactory;
4449

45-
public ArangoExampleConverter(final ArangoMappingContext context) {
50+
public ArangoExampleConverter(final ArangoMappingContext context, final ResolverFactory resolverFactory) {
4651
this.context = context;
52+
this.resolverFactory = resolverFactory;
4753
}
4854

4955
public String convertExampleToPredicate(final Example<T> example, final Map<String, Object> bindVars) {
5056
final StringBuilder predicateBuilder = new StringBuilder();
5157
final ArangoPersistentEntity<?> persistentEntity = context.getPersistentEntity(example.getProbeType());
5258
Assert.isTrue(example.getProbe() != null, "Probe in Example cannot be null");
53-
traversePropertyTree(example, predicateBuilder, bindVars, "", "", persistentEntity, example.getProbe());
59+
final String bindEntintyName = "e";
60+
traversePropertyTree(example, predicateBuilder, bindVars, "", "", persistentEntity, example.getProbe(),
61+
bindEntintyName);
5462
return predicateBuilder.toString();
5563
}
5664

57-
private void traversePropertyTree(
58-
final Example<T> example,
59-
final StringBuilder predicateBuilder,
60-
final Map<String, Object> bindVars,
61-
final String path,
62-
final String javaPath,
63-
final ArangoPersistentEntity<?> entity,
64-
final Object object) {
65+
private void traversePropertyTree(final Example<T> example, final StringBuilder predicateBuilder,
66+
final Map<String, Object> bindVars, final String path, final String javaPath,
67+
final ArangoPersistentEntity<?> entity, final Object object, final String bindEntintyName) {
6568
final PersistentPropertyAccessor<?> accessor = entity.getPropertyAccessor(object);
6669
entity.doWithProperties((final ArangoPersistentProperty property) -> {
6770
if (property.getFrom().isPresent() || property.getTo().isPresent() || property.getRelations().isPresent()) {
@@ -70,13 +73,35 @@ private void traversePropertyTree(
7073
final String fullPath = path + (path.length() == 0 ? "" : ".") + property.getFieldName();
7174
final String fullJavaPath = javaPath + (javaPath.length() == 0 ? "" : ".") + property.getName();
7275
final Object value = accessor.getProperty(property);
73-
if (property.isEntity() && value != null) {
76+
77+
if (property.isCollectionLike() && value != null) {
78+
final ArangoPersistentEntity<?> persistentEntity = context
79+
.getPersistentEntity(property.getActualType());
80+
final StringBuilder predicateBuilderArray = new StringBuilder();
81+
for (Object item : (Iterable) value) {
82+
final StringBuilder predicateBuilderArrayItem = new StringBuilder();
83+
traversePropertyTree(example, predicateBuilderArrayItem, bindVars, "", fullJavaPath,
84+
persistentEntity, item, "CURRENT");
85+
if (predicateBuilderArray.length() > 0) {
86+
predicateBuilderArray.append(" OR ");
87+
}
88+
predicateBuilderArray.append(predicateBuilderArrayItem.toString());
89+
}
90+
final String delimiter = example.getMatcher().isAllMatching() ? " AND " : " OR ";
91+
if (predicateBuilder.length() > 0) {
92+
predicateBuilder.append(delimiter);
93+
}
94+
String clause = String.format("LENGTH(%s.%s[* FILTER %s ])>0", bindEntintyName, property.getName(),
95+
predicateBuilderArray.toString());
96+
predicateBuilder.append(clause);
97+
98+
} else if (property.isEntity() && value != null) {
7499
final ArangoPersistentEntity<?> persistentEntity = context.getPersistentEntity(property.getType());
75100
traversePropertyTree(example, predicateBuilder, bindVars, fullPath, fullJavaPath, persistentEntity,
76-
value);
101+
value, bindEntintyName);
77102
} else if (!example.getMatcher().isIgnoredPath(fullJavaPath) && (value != null
78103
|| example.getMatcher().getNullHandler().equals(ExampleMatcher.NullHandler.INCLUDE))) {
79-
addPredicate(example, predicateBuilder, bindVars, fullPath, fullJavaPath, value);
104+
addPredicate(example, predicateBuilder, bindVars, fullPath, fullJavaPath, value, bindEntintyName);
80105
}
81106
});
82107

@@ -91,20 +116,22 @@ private void traversePropertyTree(
91116
final ArangoPersistentEntity<?> persistentEntity = context.getPersistentEntity(property.getType());
92117
final PersistentPropertyAccessor<?> associatedAccessor = persistentEntity.getPropertyAccessor(value);
93118
final Object idValue = associatedAccessor.getProperty(persistentEntity.getIdProperty());
94-
95-
String refIdValue = String.format("%s/%s", persistentEntity.getCollection(), idValue);
96-
addPredicate(example, predicateBuilder, bindVars, fullPath, fullJavaPath, refIdValue);
119+
String refIdValue = null;
120+
if (property.getRef().isPresent()) {
121+
final Optional<ReferenceResolver<Annotation>> resolver = resolverFactory
122+
.getReferenceResolver(property.getRef().get());
123+
refIdValue = resolver.get().write(value, persistentEntity, idValue, property.getRef().get());
124+
} else {
125+
refIdValue = String.format("%s/%s", persistentEntity.getCollection(), idValue);
126+
}
127+
addPredicate(example, predicateBuilder, bindVars, fullPath, fullJavaPath, refIdValue, bindEntintyName);
97128
}
98129
});
99130
}
100131

101-
private void addPredicate(
102-
final Example<T> example,
103-
final StringBuilder predicateBuilder,
104-
final Map<String, Object> bindVars,
105-
final String fullPath,
106-
final String fullJavaPath,
107-
Object value) {
132+
private void addPredicate(final Example<T> example, final StringBuilder predicateBuilder,
133+
final Map<String, Object> bindVars, final String fullPath, final String fullJavaPath, Object value,
134+
final String bindEntintyName) {
108135
final String delimiter = example.getMatcher().isAllMatching() ? " AND " : " OR ";
109136
if (predicateBuilder.length() > 0) {
110137
predicateBuilder.append(delimiter);
@@ -117,7 +144,7 @@ private void addPredicate(
117144
value = specifier.transformValue(Optional.of(value)).orElse(null);
118145
}
119146
if (value == null) {
120-
clause = String.format("e.%s == null", fullPath);
147+
clause = String.format("%s.%s == null", bindEntintyName, fullPath);
121148
} else if (String.class.isAssignableFrom(value.getClass())) {
122149
final boolean ignoreCase = specifier == null ? example.getMatcher().isIgnoreCaseEnabled()
123150
: (specifier.getIgnoreCase() == null ? false : specifier.getIgnoreCase());
@@ -127,10 +154,10 @@ private void addPredicate(
127154
: specifier.getStringMatcher();
128155
final String string = (String) value;
129156
if (stringMatcher == ExampleMatcher.StringMatcher.REGEX) {
130-
clause = String.format("REGEX_TEST(e.%s, @%s, %b)", fullPath, binding, ignoreCase);
131-
} else {
132-
clause = String.format("LIKE(e.%s, @%s, %b)", fullPath, binding, ignoreCase);
133-
}
157+
clause = String.format("REGEX_TEST(%s.%s, @%s, %b)", bindEntintyName, fullPath, binding, ignoreCase);
158+
} else {
159+
clause = String.format("LIKE(%s.%s, @%s, %b)", bindEntintyName, fullPath, binding, ignoreCase);
160+
}
134161
switch (stringMatcher) {
135162
case STARTING:
136163
value = escape(string) + "%";
@@ -142,16 +169,16 @@ private void addPredicate(
142169
value = "%" + escape(string) + "%";
143170
break;
144171
case REGEX:
145-
value = escape(string);
146-
break;
172+
value = escape(string);
173+
break;
147174
case DEFAULT:
148175
case EXACT:
149176
default:
150177
value = escape(string);
151178
break;
152179
}
153180
} else {
154-
clause = "e." + fullPath + " == @" + binding;
181+
clause = String.format("%s.%s == @%s", bindEntintyName, fullPath, binding);
155182
}
156183
predicateBuilder.append(clause);
157184
if (value != null) {

0 commit comments

Comments
 (0)