Skip to content

Commit 7699c01

Browse files
committed
jnosql-arangodb: implement GraphDatabaseManager
1 parent ca14407 commit 7699c01

File tree

4 files changed

+209
-55
lines changed

4 files changed

+209
-55
lines changed

jnosql-arangodb/src/main/java/org/eclipse/jnosql/databases/arangodb/communication/ArangoDBUtil.java

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,10 @@
3232
import org.eclipse.jnosql.communication.semistructured.CommunicationEntity;
3333
import org.eclipse.jnosql.communication.semistructured.Element;
3434

35-
import java.util.Collection;
36-
import java.util.Collections;
37-
import java.util.List;
38-
import java.util.Map;
39-
import java.util.Objects;
35+
import java.util.*;
4036
import java.util.logging.Level;
4137
import java.util.logging.Logger;
38+
import java.util.stream.Collectors;
4239

4340
import static java.util.stream.Collectors.toList;
4441
import static java.util.stream.StreamSupport.stream;
@@ -51,12 +48,17 @@ public final class ArangoDBUtil {
5148
public static final String KEY = "_key";
5249
public static final String ID = "_id";
5350
public static final String REV = "_rev";
51+
public static final String FROM = "_from";
52+
public static final String TO = "_to";
53+
private static final Set<String> META_FIELDS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
54+
ID, KEY, REV, FROM, TO
55+
)));
56+
5457
private static final Logger LOGGER = Logger.getLogger(ArangoDBUtil.class.getName());
5558

5659
private ArangoDBUtil() {
5760
}
5861

59-
6062
static void checkDatabase(String database, ArangoDB arangoDB) {
6163
Objects.requireNonNull(database, "database is required");
6264
try {
@@ -94,10 +96,27 @@ static CommunicationEntity toEntity(JsonObject jsonObject) {
9496
documents.add(Element.of(KEY, jsonObject.getString(KEY)));
9597
documents.add(Element.of(ID, id));
9698
documents.add(Element.of(REV, jsonObject.getString(REV)));
99+
if (jsonObject.containsKey(FROM)) {
100+
documents.add(Element.of(FROM, jsonObject.getString(FROM)));
101+
}
102+
if (jsonObject.containsKey(TO)) {
103+
documents.add(Element.of(TO, jsonObject.getString(TO)));
104+
}
97105
String collection = id.split("/")[0];
98106
return CommunicationEntity.of(collection, documents);
99107
}
100108

109+
static ArangoDBCommunicationEdge toEdge(JsonObject edge, JsonObject from, JsonObject to) {
110+
String id = edge.getString(ID);
111+
String label = id.split("/")[0];
112+
Map<String, Object> properties = edge.entrySet().stream()
113+
.filter(e -> !META_FIELDS.contains(e.getKey()))
114+
.map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), toDocuments(e.getValue())))
115+
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
116+
117+
return new ArangoDBCommunicationEdge(id, toEntity(edge), toEntity(from), label, properties);
118+
}
119+
101120
static JsonObject toJsonObject(CommunicationEntity entity) {
102121
return toJsonObject(entity.elements());
103122
}

jnosql-arangodb/src/main/java/org/eclipse/jnosql/databases/arangodb/communication/DefaultArangoDBDocumentManager.java

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,6 @@ public <T> Stream<T> aql(String query, Class<T> type) {
169169
return db.query(query, type, emptyMap(), null).stream();
170170
}
171171

172-
173172
@Override
174173
public void close() {
175174
db.arango().shutdown();
@@ -232,13 +231,13 @@ public CommunicationEdge edge(CommunicationEntity source, String label, Communic
232231
target = ensureEntityExists(target);
233232

234233
CommunicationEntity entity = CommunicationEntity.of(label);
235-
entity.add(FROM, extractId(source));
236-
entity.add(TO, extractId(target));
234+
entity.add(FROM, extractId(source).orElseThrow());
235+
entity.add(TO, extractId(target).orElseThrow());
237236
properties.forEach(entity::add);
238237

239238
JsonObject jsonObject = ArangoDBUtil.toJsonObject(entity);
240-
String key = db.collection(label).insertDocument(jsonObject).getKey();
241-
return new ArangoDBCommunicationEdge(key, source, target, label, properties);
239+
String id = db.collection(label).insertDocument(jsonObject).getId();
240+
return new ArangoDBCommunicationEdge(id, source, target, label, properties);
242241
}
243242

244243
private CommunicationEntity ensureEntityExists(CommunicationEntity entity) {
@@ -250,17 +249,76 @@ private CommunicationEntity ensureEntityExists(CommunicationEntity entity) {
250249

251250
@Override
252251
public void remove(CommunicationEntity source, String label, CommunicationEntity target) {
253-
throw new UnsupportedOperationException("TODO");
252+
Objects.requireNonNull(source, "Source entity is required");
253+
Objects.requireNonNull(target, "Target entity is required");
254+
Objects.requireNonNull(label, "Relationship type is required");
255+
256+
String sourceId = extractId(source).orElseThrow();
257+
String targetId = extractId(target).orElseThrow();
258+
259+
db.query("""
260+
FOR e IN @@collection
261+
FILTER e._from == @source AND e._to == @target
262+
REMOVE e IN @@collection
263+
""", Void.class, Map.of(
264+
"@collection", label,
265+
"source", sourceId,
266+
"target", targetId
267+
));
254268
}
255269

256270
@Override
257271
public <K> void deleteEdge(K id) {
258-
throw new UnsupportedOperationException("TODO");
272+
if (!(id instanceof String idString)) {
273+
throw new IllegalArgumentException("The id must be a String");
274+
}
275+
var elements = idString.split("/");
276+
if (elements.length != 2) {
277+
throw new IllegalArgumentException("The id must be in the format collection/key");
278+
}
279+
String collection = elements[0];
280+
String key = elements[1];
281+
String query = """
282+
FOR e IN @@collection
283+
FILTER e._key == @key
284+
REMOVE e IN @@collection
285+
""";
286+
var bindVars = Map.of(
287+
"@collection", collection,
288+
"key", key);
289+
db.query(query, Void.class, bindVars);
259290
}
260291

261292
@Override
262293
public <K> Optional<CommunicationEdge> findEdgeById(K id) {
263-
throw new UnsupportedOperationException("TODO");
294+
if (!(id instanceof String idString)) {
295+
throw new IllegalArgumentException("The id must be a String");
296+
}
297+
var elements = idString.split("/");
298+
if (elements.length != 2) {
299+
throw new IllegalArgumentException("The id must be in the format collection/key");
300+
}
301+
String collection = elements[0];
302+
String key = elements[1];
303+
String query = """
304+
FOR e IN @@collection
305+
FILTER e._key == @key
306+
RETURN {
307+
edge: e,
308+
source: DOCUMENT(e._from),
309+
target: DOCUMENT(e._to)
310+
}
311+
""";
312+
var bindVars = Map.of(
313+
"@collection", collection,
314+
"key", key);
315+
return db.query(query, JsonObject.class, bindVars)
316+
.stream()
317+
.findFirst()
318+
.map(it-> ArangoDBUtil.toEdge(
319+
it.getJsonObject("edge"),
320+
it.getJsonObject("source"),
321+
it.getJsonObject("target")));
264322
}
265323

266324
private Optional<String> extractId(CommunicationEntity entity) {

jnosql-arangodb/src/main/java/org/eclipse/jnosql/databases/arangodb/mapping/DefaultArangoDBTemplate.java

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,15 @@
1919
import jakarta.enterprise.inject.Instance;
2020
import jakarta.enterprise.inject.Typed;
2121
import jakarta.inject.Inject;
22-
import org.eclipse.jnosql.communication.semistructured.DatabaseManager;
22+
import org.eclipse.jnosql.communication.graph.GraphDatabaseManager;
2323
import org.eclipse.jnosql.databases.arangodb.communication.ArangoDBDocumentManager;
2424
import org.eclipse.jnosql.mapping.core.Converters;
25-
import org.eclipse.jnosql.mapping.graph.Edge;
25+
import org.eclipse.jnosql.mapping.graph.AbstractGraphTemplate;
2626
import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata;
27-
import org.eclipse.jnosql.mapping.semistructured.AbstractSemiStructuredTemplate;
2827
import org.eclipse.jnosql.mapping.semistructured.EntityConverter;
2928
import org.eclipse.jnosql.mapping.semistructured.EventPersistManager;
3029

3130
import java.util.Map;
32-
import java.util.Optional;
33-
import java.util.function.Supplier;
3431
import java.util.stream.Stream;
3532

3633
import static java.util.Objects.requireNonNull;
@@ -40,17 +37,17 @@
4037
*/
4138
@Typed(ArangoDBTemplate.class)
4239
@ApplicationScoped
43-
class DefaultArangoDBTemplate extends AbstractSemiStructuredTemplate implements ArangoDBTemplate {
40+
class DefaultArangoDBTemplate extends AbstractGraphTemplate implements ArangoDBTemplate {
4441

4542
private final Instance<ArangoDBDocumentManager> manager;
4643

47-
private final EntityConverter converter;
44+
private final EntityConverter converter;
4845

49-
private final EventPersistManager eventManager;
46+
private final EventPersistManager eventManager;
5047

51-
private final EntitiesMetadata entities;
48+
private final EntitiesMetadata entities;
5249

53-
private final Converters converters;
50+
private final Converters converters;
5451

5552
@Inject
5653
DefaultArangoDBTemplate(Instance<ArangoDBDocumentManager> manager,
@@ -75,7 +72,7 @@ protected EntityConverter converter() {
7572
}
7673

7774
@Override
78-
protected DatabaseManager manager() {
75+
protected GraphDatabaseManager manager() {
7976
return manager.get();
8077
}
8178

@@ -112,33 +109,4 @@ public <T> Stream<T> aql(String query, Class<T> type) {
112109
return manager.get().aql(query, type);
113110
}
114111

115-
@Override
116-
public <T, E> Edge<T, E> edge(T source, String label, E target, Map<String, Object> properties) {
117-
throw new UnsupportedOperationException("TODO");
118-
}
119-
120-
@Override
121-
public <T, E> Edge<T, E> edge(T source, Supplier<String> label, E target, Map<String, Object> properties) {
122-
throw new UnsupportedOperationException("TODO");
123-
}
124-
125-
@Override
126-
public <T, E> Edge<T, E> edge(Edge<T, E> edge) {
127-
throw new UnsupportedOperationException("TODO");
128-
}
129-
130-
@Override
131-
public <T, E> void delete(Edge<T, E> edge) {
132-
throw new UnsupportedOperationException("TODO");
133-
}
134-
135-
@Override
136-
public <K> void deleteEdge(K id) {
137-
throw new UnsupportedOperationException("TODO");
138-
}
139-
140-
@Override
141-
public <K, T, E> Optional<Edge<T, E>> findEdgeById(K id) {
142-
throw new UnsupportedOperationException("TODO");
143-
}
144112
}

jnosql-arangodb/src/test/java/org/eclipse/jnosql/databases/arangodb/communication/ArangoDBDocumentManagerTest.java

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.arangodb.ArangoDB;
1919
import org.assertj.core.api.SoftAssertions;
2020
import org.eclipse.jnosql.communication.TypeReference;
21+
import org.eclipse.jnosql.communication.graph.CommunicationEdge;
2122
import org.eclipse.jnosql.communication.semistructured.CommunicationEntity;
2223
import org.eclipse.jnosql.communication.semistructured.CriteriaCondition;
2324
import org.eclipse.jnosql.communication.semistructured.DeleteQuery;
@@ -42,6 +43,7 @@
4243
import java.util.stream.Collectors;
4344

4445
import static java.util.Arrays.asList;
46+
import static java.util.Collections.emptyMap;
4547
import static java.util.Collections.singletonMap;
4648
import static org.assertj.core.api.Assertions.assertThat;
4749
import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES;
@@ -452,6 +454,114 @@ void shouldEndsWith() {
452454
});
453455
}
454456

457+
@Test
458+
void shouldCreateEdge() {
459+
var person1 = entityManager.insert(getEntity());
460+
var person2 = entityManager.insert(getEntity());
461+
462+
String person1Id = entityManager.select(select().from(COLLECTION_NAME)
463+
.where("_key").eq(person1.find("_key").orElseThrow().get()).build())
464+
.findFirst().orElseThrow().find("_id").orElseThrow().get(String.class);
465+
466+
String person2Id = entityManager.select(select().from(COLLECTION_NAME)
467+
.where("_key").eq(person2.find("_key").orElseThrow().get()).build())
468+
.findFirst().orElseThrow().find("_id").orElseThrow().get(String.class);
469+
470+
entityManager.edge(person1, "FRIEND", person2, emptyMap());
471+
472+
String aql = """
473+
FOR e IN FRIEND
474+
FILTER e._from == @id1 && e._to == @id2
475+
RETURN e
476+
""";
477+
478+
Map<String, Object> parameters = Map.of(
479+
"id1", person1Id,
480+
"id2", person2Id
481+
);
482+
483+
var result = entityManager.aql(aql, parameters).toList();
484+
SoftAssertions.assertSoftly(softly -> softly.assertThat(result).isNotEmpty());
485+
486+
entityManager.remove(person1, "FRIEND", person2);
487+
}
488+
489+
@Test
490+
void shouldRemoveEdge() {
491+
var person1 = getEntity();
492+
var person2 = getEntity();
493+
494+
entityManager.insert(person1);
495+
entityManager.insert(person2);
496+
497+
CommunicationEdge edge = entityManager.edge(person1, "FRIEND", person2, emptyMap());
498+
entityManager.remove(person1, "FRIEND", person2);
499+
500+
String aql = """
501+
FOR e IN FRIEND
502+
FILTER e._key == @edgeId
503+
RETURN e
504+
""";
505+
Map<String, Object> parameters = Map.of("edgeId", edge.id());
506+
var result = entityManager.aql(aql, parameters).toList();
507+
SoftAssertions.assertSoftly(softly -> softly.assertThat(result).isEmpty());
508+
}
509+
510+
@Test
511+
void shouldDeleteEdgeById() {
512+
var person1 = entityManager.insert(getEntity());
513+
var person2 = entityManager.insert(getEntity());
514+
var edge = entityManager.edge(person1, "FRIEND", person2, Map.of("since", 2020));
515+
entityManager.deleteEdge(edge.id());
516+
String aql = """
517+
FOR e IN FRIEND
518+
FILTER e._id == @id
519+
RETURN e
520+
""";
521+
Map<String, Object> parameters = Map.of("id", edge.id());
522+
var result = entityManager.aql(aql, parameters).toList();
523+
SoftAssertions.assertSoftly(softly -> softly.assertThat(result).isEmpty());
524+
}
525+
526+
@Test
527+
void shouldFindEdgeById() {
528+
var person1 = entityManager.insert(getEntity());
529+
var person2 = entityManager.insert(getEntity());
530+
531+
var edge = entityManager.edge(person1, "FRIEND", person2, Map.of("since", 2020));
532+
var edgeId = edge.id();
533+
var retrievedEdge = entityManager.findEdgeById(edgeId);
534+
535+
SoftAssertions.assertSoftly(softly -> {
536+
softly.assertThat(retrievedEdge).isPresent();
537+
softly.assertThat(retrievedEdge.get().label()).isEqualTo("FRIEND");
538+
softly.assertThat(retrievedEdge.get().properties()).containsEntry("since", 2020);
539+
});
540+
}
541+
542+
@Test
543+
void shouldCreateEdgeWithProperties() {
544+
var person1 = entityManager.insert(getEntity());
545+
var person2 = entityManager.insert(getEntity());
546+
547+
Map<String, Object> properties = Map.of("since", 2019, "strength", "strong");
548+
var edge = entityManager.edge(person1, "FRIEND", person2, properties);
549+
550+
String aql = """
551+
FOR e IN FRIEND
552+
FILTER e._id == @edgeId
553+
RETURN e
554+
""";
555+
Map<String, Object> parameters = Map.of("edgeId", edge.id());
556+
557+
var result = entityManager.aql(aql, parameters).toList();
558+
SoftAssertions.assertSoftly(softly -> {
559+
softly.assertThat(result).isNotEmpty();
560+
softly.assertThat(edge.properties()).containsEntry("since", 2019);
561+
softly.assertThat(edge.properties()).containsEntry("strength", "strong");
562+
});
563+
}
564+
455565
private CommunicationEntity getEntity() {
456566
CommunicationEntity entity = CommunicationEntity.of(COLLECTION_NAME);
457567
Map<String, Object> map = new HashMap<>();
@@ -483,7 +593,6 @@ private CommunicationEntity createDocumentList() {
483593
}
484594

485595
private CommunicationEntity createDocumentListNotHavingId() {
486-
String id = UUID.randomUUID().toString();
487596
CommunicationEntity entity = CommunicationEntity.of("AppointmentBook");
488597
entity.add(Element.of("_id", "ids"));
489598
List<List<Element>> documents = new ArrayList<>();

0 commit comments

Comments
 (0)