Skip to content

ArangoDB: use _key instead of _id #293

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

Merged
merged 4 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions jnosql-arangodb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<description>The Eclipse JNoSQL layer to ArangoDB</description>

<properties>
<arango.driver>7.7.1</arango.driver>
<arango.driver>7.11.0</arango.driver>
</properties>
<dependencies>
<dependency>
Expand All @@ -46,7 +46,7 @@
</dependency>
<dependency>
<groupId>com.arangodb</groupId>
<artifactId>arangodb-java-driver</artifactId>
<artifactId>arangodb-java-driver-shaded</artifactId>
<version>${arango.driver}</version>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
* Contributors:
*
* Otavio Santana
* Michele Rastelli
*/
package org.eclipse.jnosql.databases.arangodb.communication;


import com.arangodb.ArangoDB;
import com.arangodb.entity.BaseDocument;
import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.json.bind.Jsonb;
import org.eclipse.jnosql.communication.Value;
import org.eclipse.jnosql.communication.driver.JsonbSupplier;
Expand All @@ -41,8 +43,9 @@
public class ArangoDBBucketManager implements BucketManager {


private static final String KEY = "_key";
private static final String VALUE = "_value";
private static final Function<BaseDocument, String> TO_JSON = e -> e.getAttribute(VALUE).toString();
private static final Function<JsonObject, String> TO_JSON = e -> e.getString(VALUE);
private static final Jsonb JSONB = JsonbSupplier.getInstance().get();

private final ArangoDB arangoDB;
Expand All @@ -66,14 +69,15 @@ public String name() {
public <K, V> void put(K key, V value) throws NullPointerException {
Objects.requireNonNull(key, "Key is required");
Objects.requireNonNull(value, "value is required");
BaseDocument baseDocument = new BaseDocument();
baseDocument.setKey(key.toString());
baseDocument.addAttribute(VALUE, JSONB.toJson(value));
JsonObject jsonObject = Json.createObjectBuilder()
.add(KEY, key.toString())
.add(VALUE, JSONB.toJson(value))
.build();
if (arangoDB.db(bucketName).collection(namespace).documentExists(key.toString())) {
arangoDB.db(bucketName).collection(namespace).deleteDocument(key.toString());
}
arangoDB.db(bucketName).collection(namespace)
.insertDocument(baseDocument);
.insertDocument(jsonObject);
}

@Override
Expand All @@ -91,8 +95,8 @@ public void put(Iterable<KeyValueEntity> keyValueEntities) throws NullPointerEx
@Override
public <K> Optional<Value> get(K key) throws NullPointerException {
Objects.requireNonNull(key, "Key is required");
BaseDocument entity = arangoDB.db(bucketName).collection(namespace)
.getDocument(key.toString(), BaseDocument.class);
JsonObject entity = arangoDB.db(bucketName).collection(namespace)
.getDocument(key.toString(), JsonObject.class);

return ofNullable(entity)
.map(TO_JSON)
Expand All @@ -105,7 +109,7 @@ public <K> Iterable<Value> get(Iterable<K> keys) throws NullPointerException {
return stream(keys.spliterator(), false)
.map(Object::toString)
.map(k -> arangoDB.db(bucketName).collection(namespace)
.getDocument(k, BaseDocument.class))
.getDocument(k, JsonObject.class))
.filter(Objects::nonNull)
.map(TO_JSON)
.map(ValueJSON::of)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,27 @@
* Contributors:
*
* Otavio Santana
* Michele Rastelli
*/
package org.eclipse.jnosql.databases.arangodb.communication;


import com.arangodb.ArangoDB;
import com.arangodb.entity.LoadBalancingStrategy;
import com.arangodb.serde.ArangoSerde;
import org.eclipse.jnosql.communication.Settings;

import static java.util.Objects.requireNonNull;

/**
* The base to configuration both key-value and document on mongoDB.
* The base to configuration both key-value and document on ArangoDB.
* To each configuration set, it will change both builder
* {@link ArangoDB.Builder}
*/
public abstract class ArangoDBConfiguration {


protected ArangoDB.Builder builder = new ArangoDB.Builder();
protected ArangoDB.Builder builder = new ArangoDB.Builder()
.serde(new JsonbSerde());

/**
* Adds a host in the arangodb builder
Expand All @@ -54,7 +56,6 @@ public void setLoadBalancingStrategy(LoadBalancingStrategy loadBalancingStrategy
builder.loadBalancingStrategy(loadBalancingStrategy);
}


/**
* set the setTimeout
*
Expand Down Expand Up @@ -91,6 +92,21 @@ public void setUseSSL(boolean value) {
builder.useSsl(value);
}

/**
* Set the ArangoDB serde for the user data. Note that the provided
* serde must support serializing and deserializing JsonP types,
* i.e. {@link jakarta.json.JsonValue} and its children.
* By default, the builder is configured to use {@link JsonbSerde};
* this setter allows overriding it, i.e. providing an instance of
* {@link JsonbSerde} that uses a specific {@link jakarta.json.bind.Jsonb}
* instance.
*
* @param serde the serde
*/
public void setSerde(ArangoSerde serde) {
builder.serde(serde);
}

/**
* Defines a new builder to sync ArangoDB
*
Expand All @@ -102,12 +118,10 @@ public void syncBuilder(ArangoDB.Builder builder) throws NullPointerException {
this.builder = builder;
}


protected ArangoDB getArangoDB(Settings settings) {
ArangoDBBuilderSync aragonDB = new ArangoDBBuilderSync(builder);
ArangoDBBuilders.load(settings, aragonDB);
return aragonDB.build();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,33 @@
* Contributors:
*
* Otavio Santana
* Michele Rastelli
*/
package org.eclipse.jnosql.databases.arangodb.communication;


import com.arangodb.ArangoDB;
import com.arangodb.entity.BaseDocument;
import com.arangodb.entity.CollectionEntity;
import org.eclipse.jnosql.communication.Value;
import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonArrayBuilder;
import jakarta.json.JsonNumber;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;
import jakarta.json.JsonString;
import jakarta.json.JsonValue;
import org.eclipse.jnosql.communication.ValueUtil;
import org.eclipse.jnosql.communication.semistructured.CommunicationEntity;
import org.eclipse.jnosql.communication.semistructured.Element;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;

import static java.util.Collections.singletonMap;
import static java.util.stream.Collectors.toList;
import static java.util.stream.StreamSupport.stream;

Expand All @@ -47,9 +51,6 @@ public final class ArangoDBUtil {
public static final String REV = "_rev";
private static final Logger LOGGER = Logger.getLogger(ArangoDBUtil.class.getName());

private static final Function<Map.Entry<?, ?>, Element> ENTRY_DOCUMENT = entry ->
Element.of(entry.getKey().toString(), entry.getValue());

private ArangoDBUtil() {
}

Expand All @@ -71,101 +72,100 @@ public static void checkCollection(String bucketName, ArangoDB arangoDB, String
List<String> collections = arangoDB.db(bucketName)
.getCollections().stream()
.map(CollectionEntity::getName)
.collect(toList());
.toList();
if (!collections.contains(namespace)) {
arangoDB.db(bucketName).createCollection(namespace);
}
}

static CommunicationEntity toEntity(JsonObject jsonObject) {
List<Element> documents = toDocuments(jsonObject);

static CommunicationEntity toEntity(BaseDocument document) {
Map<String, Object> properties = document.getProperties();
List<Element> documents = properties.keySet().stream()
.map(k -> toDocument(k, properties))
.collect(toList());

documents.add(Element.of(KEY, document.getKey()));
documents.add(Element.of(ID, document.getId()));
documents.add(Element.of(REV, document.getRevision()));
String collection = document.getId().split("/")[0];
String id = jsonObject.getString(ID);
documents.add(Element.of(KEY, jsonObject.getString(KEY)));
documents.add(Element.of(ID, id));
documents.add(Element.of(REV, jsonObject.getString(REV)));
String collection = id.split("/")[0];
return CommunicationEntity.of(collection, documents);
}

static BaseDocument getBaseDocument(CommunicationEntity entity) {
Map<String, Object> map = new HashMap<>();
for (Element document : entity.elements()) {
if(KEY.equals(document.name()) && Objects.isNull(document.get())) {
continue;
}
map.put(document.name(), convert(document.value()));
}
return new BaseDocument(map);
static JsonObject toJsonObject(CommunicationEntity entity) {
return toJsonObject(entity.elements());
}

private static Element toDocument(String key, Map<String, Object> properties) {
Object value = properties.get(key);
if (value instanceof Map map) {
return Element.of(key, map.keySet()
.stream().map(k -> toDocument(k.toString(), map))
.collect(toList()));
}
if (isADocumentIterable(value)) {
List<List<Element>> documents = new ArrayList<>();
for (Object object : Iterable.class.cast(value)) {
Map<?, ?> map = Map.class.cast(object);
documents.add(map.entrySet().stream().map(ENTRY_DOCUMENT).collect(toList()));
}
return Element.of(key, documents);

}
return Element.of(key, value);
private static List<Element> toDocuments(JsonObject object) {
return object.entrySet().stream()
.map(it -> Element.of(it.getKey(), toDocuments(it.getValue())))
.collect(toList());
}

private static boolean isADocumentIterable(Object value) {
return Iterable.class.isInstance(value) &&
stream(Iterable.class.cast(value).spliterator(), false)
.allMatch(Map.class::isInstance);
private static List<?> toDocuments(JsonArray array) {
return array.stream()
.map(ArangoDBUtil::toDocuments)
.toList();
}

private static Object convert(Value value) {
Object val = ValueUtil.convert(value);

if (Element.class.isInstance(val)) {
Element document = Element.class.cast(val);
return singletonMap(document.name(), convert(document.value()));
}
if (isSudDocument(val)) {
return getMap(val);
}
if (isSudDocumentList(val)) {
return stream(Iterable.class.cast(val).spliterator(), false)
.map(ArangoDBUtil::getMap).collect(toList());
}
return val;
private static Object toDocuments(JsonValue value) {
return switch (value.getValueType()) {
case OBJECT -> toDocuments(value.asJsonObject());
case ARRAY -> toDocuments(value.asJsonArray());
case STRING -> ((JsonString) value).getString();
case NUMBER -> ((JsonNumber) value).numberValue();
case TRUE -> true;
case FALSE -> false;
case NULL -> null;
};
}

private static Object getMap(Object val) {
Iterable<?> iterable = Iterable.class.cast(val);
Map<Object, Object> map = new HashMap<>();
for (Object item : iterable) {
var document = cast(item);
map.put(document.name(), document.get());
private static JsonObject toJsonObject(Iterable<Element> elements) {
JsonObjectBuilder builder = Json.createObjectBuilder();
for (Element document : elements) {
if (KEY.equals(document.name()) && Objects.isNull(document.get())) {
continue;
}
Object value = ValueUtil.convert(document.value());
builder.add(document.name(), toJsonValue(value));
}
return map;
return builder.build();
}

private static boolean isSudDocumentList(Object value) {
return value instanceof Iterable && stream(Iterable.class.cast(value).spliterator(), false).
allMatch(d -> d instanceof Iterable && isSudDocument(d));
}

private static boolean isSudDocument(Object value) {
return value instanceof Iterable && stream(Iterable.class.cast(value).spliterator(), false).
allMatch(Element.class::isInstance);
@SuppressWarnings("unchecked")
private static JsonValue toJsonValue(Object value) {
if (value instanceof Element document) {
return toJsonObject(Collections.singletonList(document));
} else if (value instanceof Iterable<?> iterable) {
if (isSubDocument(iterable)) {
return toJsonObject((Iterable<Element>) iterable);
} else {
JsonArrayBuilder builder = Json.createArrayBuilder();
for (Object it : iterable) {
builder.add(toJsonValue(it));
}
return builder.build();
}
} else if (value instanceof Map<?, ?> map) {
JsonObjectBuilder builder = Json.createObjectBuilder();
for (Map.Entry<?, ?> e : map.entrySet()) {
builder.add((String) e.getKey(), toJsonValue(e.getValue()));
}
return builder.build();
} else if (Objects.isNull(value)) {
return JsonValue.NULL;
} else if (value instanceof Number number) {
return Json.createValue(number);
} else if (value instanceof String string) {
return Json.createValue(string);
} else if (Boolean.TRUE.equals(value)) {
return JsonValue.TRUE;
} else if (Boolean.FALSE.equals(value)) {
return JsonValue.FALSE;
} else {
throw new IllegalArgumentException("Unsupported type: " + value.getClass());
}
}

private static Element cast(Object document) {
return Element.class.cast(document);
private static boolean isSubDocument(Iterable<?> iterable) {
return stream(iterable.spliterator(), false).allMatch(Element.class::isInstance);
}

}
Loading