diff --git a/jnosql-arangodb/src/main/java/org/eclipse/jnosql/databases/arangodb/communication/DefaultArangoDBDocumentManager.java b/jnosql-arangodb/src/main/java/org/eclipse/jnosql/databases/arangodb/communication/DefaultArangoDBDocumentManager.java index 83daf7375..b30297d06 100644 --- a/jnosql-arangodb/src/main/java/org/eclipse/jnosql/databases/arangodb/communication/DefaultArangoDBDocumentManager.java +++ b/jnosql-arangodb/src/main/java/org/eclipse/jnosql/databases/arangodb/communication/DefaultArangoDBDocumentManager.java @@ -27,6 +27,7 @@ import java.time.Duration; import java.util.Map; +import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -76,12 +77,23 @@ public CommunicationEntity update(CommunicationEntity entity) { requireNonNull(entity, "entity is required"); String collectionName = entity.name(); checkCollection(collectionName); - entity.find(KEY, String.class) - .orElseThrow(() -> new IllegalArgumentException("The document does not provide" + - " the _key column")); + Optional keyElement = entity.find(KEY, String.class); + Optional idElement = entity.find(ID, String.class); + if (keyElement.isEmpty() && idElement.isEmpty()) { + throw new IllegalArgumentException("To update an entity is necessary to have either " + KEY + " or " + ID); + } + var key = keyElement.orElseGet(() -> { + String id = idElement.orElseThrow(); + var elements = id.split("/"); + if (elements.length == 2) { + return elements[1]; + } else { + return elements[0]; + } + }); JsonObject jsonObject = ArangoDBUtil.toJsonObject(entity); DocumentUpdateEntity arangoDocument = arangoDB.db(database) - .collection(collectionName).updateDocument(jsonObject.getString(KEY), jsonObject); + .collection(collectionName).updateDocument(key, jsonObject); updateEntity(entity, arangoDocument.getKey(), arangoDocument.getId(), arangoDocument.getRev()); return entity; } diff --git a/jnosql-arangodb/src/test/java/org/eclipse/jnosql/databases/arangodb/communication/ArangoDBDocumentManagerTest.java b/jnosql-arangodb/src/test/java/org/eclipse/jnosql/databases/arangodb/communication/ArangoDBDocumentManagerTest.java index b9d79891d..9823d669b 100644 --- a/jnosql-arangodb/src/test/java/org/eclipse/jnosql/databases/arangodb/communication/ArangoDBDocumentManagerTest.java +++ b/jnosql-arangodb/src/test/java/org/eclipse/jnosql/databases/arangodb/communication/ArangoDBDocumentManagerTest.java @@ -178,6 +178,28 @@ void shouldRetrieveListSubdocumentList() { assertTrue(contacts.stream().allMatch(d -> d.size() == 3)); } + @Test + void shouldConvertFromListSubdocumentListNotUsingKey() { + CommunicationEntity entity = createDocumentListNotHavingId(); + entityManager.insert(entity); + + } + + @Test + void shouldRetrieveListSubdocumentListNotUsingKey() { + CommunicationEntity entity = entityManager.insert(createDocumentListNotHavingId()); + Element key = entity.find(KEY_NAME).get(); + SelectQuery query = select().from("AppointmentBook").where(key.name()).eq(key.get()).build(); + + CommunicationEntity documentEntity = entityManager.singleResult(query).get(); + assertNotNull(documentEntity); + + List> contacts = (List>) documentEntity.find("contacts").get().get(); + + assertEquals(3, contacts.size()); + assertTrue(contacts.stream().allMatch(d -> d.size() == 3)); + } + @Test void shouldRunAQL() { CommunicationEntity entity = getEntity(); @@ -412,6 +434,25 @@ private CommunicationEntity createDocumentList() { return entity; } + private CommunicationEntity createDocumentListNotHavingId() { + String id = UUID.randomUUID().toString(); + CommunicationEntity entity = CommunicationEntity.of("AppointmentBook"); + entity.add(Element.of("_id", "ids")); + List> documents = new ArrayList<>(); + + documents.add(asList(Element.of("name", "Ada"), Element.of("type", ContactType.EMAIL), + Element.of("information", "ada@lovelace.com"))); + + documents.add(asList(Element.of("name", "Ada"), Element.of("type", ContactType.MOBILE), + Element.of("information", "11 1231231 123"))); + + documents.add(asList(Element.of("name", "Ada"), Element.of("type", ContactType.PHONE), + Element.of("information", "phone"))); + + entity.add(Element.of("contacts", documents)); + return entity; + } + private List getEntitiesWithValues() { var lucas = CommunicationEntity.of(COLLECTION_NAME); lucas.add(Element.of("name", "Lucas")); diff --git a/jnosql-arangodb/src/test/java/org/eclipse/jnosql/databases/arangodb/integration/ArangoDBTemplateIntegrationUsingIdAnnotationTest.java b/jnosql-arangodb/src/test/java/org/eclipse/jnosql/databases/arangodb/integration/ArangoDBTemplateIntegrationUsingIdAnnotationTest.java new file mode 100644 index 000000000..2637a7335 --- /dev/null +++ b/jnosql-arangodb/src/test/java/org/eclipse/jnosql/databases/arangodb/integration/ArangoDBTemplateIntegrationUsingIdAnnotationTest.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.databases.arangodb.integration; + + +import jakarta.data.page.CursoredPage; +import jakarta.data.page.PageRequest; +import jakarta.inject.Inject; +import org.assertj.core.api.SoftAssertions; +import org.eclipse.jnosql.communication.semistructured.SelectQuery; +import org.eclipse.jnosql.databases.arangodb.communication.ArangoDBConfigurations; +import org.eclipse.jnosql.databases.arangodb.mapping.ArangoDBTemplate; +import org.eclipse.jnosql.mapping.Database; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.config.MappingConfigurations; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.document.DocumentTemplate; +import org.eclipse.jnosql.mapping.document.spi.DocumentExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import java.util.List; +import java.util.Optional; + +import static java.util.UUID.randomUUID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; +import static org.eclipse.jnosql.databases.arangodb.communication.DocumentDatabase.INSTANCE; + +@EnableAutoWeld +@AddPackages(value = {Database.class, EntityConverter.class, DocumentTemplate.class}) +@AddPackages(Article.class) +@AddPackages(ArangoDBTemplate.class) +@AddExtensions({EntityMetadataExtension.class, + DocumentExtension.class}) +@AddPackages(Reflections.class) +@AddPackages(Converters.class) +@EnabledIfSystemProperty(named = NAMED, matches = MATCHES) +class ArangoDBTemplateIntegrationUsingIdAnnotationTest { + + @Inject + private ArangoDBTemplate template; + + static { + INSTANCE.get("library"); + System.setProperty(ArangoDBConfigurations.HOST.get() + ".1", INSTANCE.host()); + System.setProperty(MappingConfigurations.DOCUMENT_DATABASE.get(), "library"); + } + + @BeforeEach + void setUp() { + this.template.delete(Article.class).execute(); + } + + @Test + void shouldInsert() { + var article = new Article(randomUUID().toString(), "Effective Java", 1); + template.insert(article); + Optional
optional = template.find(Article.class, article.id()); + assertThat(optional).isNotNull().isNotEmpty() + .get().isEqualTo(article); + } + + @Test + void shouldUpdate() { + var article = new Article(randomUUID().toString(), "Effective Java", 1); + assertThat(template.insert(article)) + .isNotNull() + .isEqualTo(article); + + Article updated = new Article(article.id(), article.title() + " updated", 2); + + assertThat(template.update(updated)) + .isNotNull() + .isNotEqualTo(article); + + assertThat(template.find(Article.class, article.id())) + .isNotNull().get().isEqualTo(updated); + + } + + @Test + void shouldFindById() { + Article article = new Article(randomUUID().toString(), "Effective Java", 1); + assertThat(template.insert(article)) + .isNotNull() + .isEqualTo(article); + + assertThat(template.find(Article.class, article.id())) + .isNotNull().get().isEqualTo(article); + } + + @Test + void shouldDelete() { + var article = new Article(randomUUID().toString(), "Effective Java", 1); + assertThat(template.insert(article)) + .isNotNull() + .isEqualTo(article); + + template.delete(Article.class, article.id()); + assertThat(template.find(Article.class, article.id())) + .isNotNull().isEmpty(); + } + + @Test + void shouldDeleteAll() { + for (int index = 0; index < 20; index++) { + var article = new Article(randomUUID().toString(), "Effective Java", 1); + assertThat(template.insert(article)) + .isNotNull() + .isEqualTo(article); + } + + template.delete(Article.class).execute(); + assertThat(template.select(Article.class).result()).isEmpty(); + } + + + @Test + void shouldUpdateNullValues() { + var article = new Article(randomUUID().toString(), "Effective Java", 1); + template.insert(article); + template.update(new Article(article.id(), null, 2)); + Optional
optional = template.select(Article.class).where("id") + .eq(article.id()).singleResult(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(optional).isPresent(); + softly.assertThat(optional).get().extracting(Article::title).isNull(); + softly.assertThat(optional).get().extracting(Article::edition).isEqualTo(2); + }); + } + + @Test + void shouldExecuteLimit() { + + for (int index = 1; index < 10; index++) { + var article = new Article(randomUUID().toString(), "Effective Java", index); + template.insert(article); + } + + List
articles = template.select(Article.class).orderBy("edition") + .asc().limit(4).result(); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(articles).hasSize(4); + var editions = articles.stream().map(Article::edition).toList(); + soft.assertThat(editions).hasSize(4).contains(1, 2, 3, 4); + }); + + } + + @Test + void shouldExecuteSkip() { + for (int index = 1; index < 10; index++) { + var book = new Article(randomUUID().toString(), "Effective Java", index); + template.insert(book); + } + + List
articles = template.select(Article.class).orderBy("edition") + .asc().skip(4).result(); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(articles).hasSize(5); + var editions = articles.stream().map(Article::edition).toList(); + soft.assertThat(editions).hasSize(5).contains(5, 6, 7, 8, 9); + }); + } + + @Test + void shouldExecuteLimitStart() { + for (int index = 1; index < 10; index++) { + var article = new Article(randomUUID().toString(), "Effective Java", index); + template.insert(article); + } + + List
articles = template.select(Article.class).orderBy("edition") + .asc().skip(4).limit(3).result(); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(articles).hasSize(3); + var editions = articles.stream().map(Article::edition).toList(); + soft.assertThat(editions).hasSize(3).contains(5, 6, 7); + }); + } + + @Test + void shouldSelectCursorSize() { + for (int index = 1; index < 10; index++) { + var article = new Article(randomUUID().toString(), "Effective Java", index); + template.insert(article); + } + var select = SelectQuery.select().from("Article").orderBy("edition").asc() + .skip(4).limit(3).build(); + var pageRequest = PageRequest.ofSize(3); + CursoredPage
entities = template.selectCursor(select, pageRequest); + + SoftAssertions.assertSoftly(soft -> { + var content = entities.content(); + soft.assertThat(content).hasSize(3); + var editions = content.stream().map(Article::edition).toList(); + soft.assertThat(editions).hasSize(3).contains(1, 2, 3); + }); + } +} diff --git a/jnosql-arangodb/src/test/java/org/eclipse/jnosql/databases/arangodb/integration/Article.java b/jnosql-arangodb/src/test/java/org/eclipse/jnosql/databases/arangodb/integration/Article.java new file mode 100644 index 000000000..7c57b8fd3 --- /dev/null +++ b/jnosql-arangodb/src/test/java/org/eclipse/jnosql/databases/arangodb/integration/Article.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.databases.arangodb.integration; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.Objects; + +@Entity +public class Article { + + @Id + private String id; + + @Column("title") + private String title; + + @Column("edition") + private int edition; + + public Article(String id, String title, int edition) { + this.id = id; + this.title = title; + this.edition = edition; + } + + Article() { + } + + public String id() { + return id; + } + + public String title() { + return title; + } + + public int edition() { + return edition; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Article magazine = (Article) o; + return edition == magazine.edition + && Objects.equals(id, magazine.id) + && Objects.equals(title, magazine.title); + } + + @Override + public int hashCode() { + return Objects.hash(id, title, edition); + } + + @Override + public String toString() { + return "Book{" + + "id='" + id + '\'' + + ", title='" + title + '\'' + + ", edition=" + edition + + '}'; + } +}