com.arangodb.springframework.repository.SimpleArangoRepository Maven / Gradle / Ivy
/*
* DISCLAIMER
*
* Copyright 2017 ArangoDB GmbH, Cologne, Germany
*
* 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.
*
* Copyright holder is ArangoDB GmbH, Cologne, Germany
*/
package com.arangodb.springframework.repository;
import com.arangodb.ArangoCursor;
import com.arangodb.ArangoDBException;
import com.arangodb.entity.DocumentDeleteEntity;
import com.arangodb.entity.ErrorEntity;
import com.arangodb.entity.MultiDocumentEntity;
import com.arangodb.model.AqlQueryOptions;
import com.arangodb.model.DocumentDeleteOptions;
import com.arangodb.springframework.core.DocumentNotFoundException;
import com.arangodb.springframework.core.convert.ArangoConverter;
import com.arangodb.springframework.core.mapping.ArangoMappingContext;
import com.arangodb.springframework.core.mapping.ArangoPersistentEntity;
import com.arangodb.springframework.core.template.ArangoTemplate;
import com.arangodb.springframework.core.util.AqlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.domain.*;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Repository;
import java.util.*;
import java.util.function.Function;
/**
* The implementation of all CRUD, paging and sorting functionality in
* ArangoRepository from the Spring Data Commons CRUD repository and
* PagingAndSorting repository
*/
@Repository
@SuppressWarnings({ "rawtypes", "unchecked" })
public class SimpleArangoRepository implements ArangoRepository {
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleArangoRepository.class);
private final ArangoTemplate arangoTemplate;
private final ArangoConverter converter;
private final ArangoMappingContext mappingContext;
private final ArangoExampleConverter exampleConverter;
private final Class domainClass;
private final boolean returnOriginalEntities;
private final ArangoPersistentEntity> persistentEntity;
/**
* @param arangoTemplate The template used to execute much of the
* functionality of this class
* @param domainClass the class type of this repository
* @param returnOriginalEntities whether save and saveAll should return the
* original entities or new ones
*/
public SimpleArangoRepository(final ArangoTemplate arangoTemplate, final Class domainClass, boolean returnOriginalEntities) {
super();
this.arangoTemplate = arangoTemplate;
this.domainClass = domainClass;
this.returnOriginalEntities = returnOriginalEntities;
converter = arangoTemplate.getConverter();
mappingContext = (ArangoMappingContext) converter.getMappingContext();
exampleConverter = new ArangoExampleConverter(mappingContext, arangoTemplate.getResolverFactory());
persistentEntity = mappingContext.getRequiredPersistentEntity(domainClass);
}
/**
* Saves the passed entity to the database using repsert from the template
*
* @param entity the entity to be saved to the database
* @return the updated entity with any id/key/rev saved
*/
@Override
public S save(final S entity) {
S saved = arangoTemplate.repsert(entity);
return returnOriginalEntities ? entity : saved;
}
/**
* Saves the given iterable of entities to the database using repsert from the template
*
* @param entities the iterable of entities to be saved to the database
* @return the iterable of updated entities with any id/key/rev saved in each
* entity
*/
@Override
public Iterable saveAll(final Iterable entities) {
Iterable saved = arangoTemplate.repsertAll(entities, domainClass);
return returnOriginalEntities ? entities : saved;
}
/**
* Finds if a document with the given id exists in the database
*
* @param id the id of the document to search for
* @return the object representing the document if found
*/
@Override
public Optional findById(final ID id) {
return arangoTemplate.find(id, domainClass);
}
/**
* Checks if a document exists or not based on the given id or key
*
* @param id represents either the key or id of a document to check for
* @return returns true if the document is found, false otherwise
*/
@Override
public boolean existsById(final ID id) {
return arangoTemplate.exists(id, domainClass);
}
/**
* Gets all documents in the collection for the class type of this repository
*
* @return an iterable with all the documents in the collection
*/
@Override
public Iterable findAll() {
return arangoTemplate.findAll(domainClass);
}
/**
* Finds all documents with the an id or key in the argument
*
* @param ids an iterable with ids/keys of documents to get
* @return an iterable with documents in the collection which have a id/key in
* the argument
*/
@Override
public Iterable findAllById(final Iterable ids) {
return arangoTemplate.findAll(ids, domainClass);
}
/**
* Counts the number of documents in the collection for the type of this
* repository
*
* @return long with number of documents
*/
@Override
public long count() {
return arangoTemplate.collection(domainClass).count();
}
/**
* Deletes the document with the given id or key
*
* @param id id or key of document to be deleted
*/
@Override
public void deleteById(final ID id) {
try {
arangoTemplate.delete(id, domainClass);
} catch (DocumentNotFoundException unknown) {
// silently ignored
}
}
/**
* Deletes document in the database representing the given object, by getting
* it's id
*
* @param entity the entity to be deleted from the database
*/
@Override
public void delete(final T entity) {
Object id = persistentEntity.getIdentifierAccessor(entity).getRequiredIdentifier();
DocumentDeleteOptions opts = new DocumentDeleteOptions();
persistentEntity.getRevProperty()
.map(persistentEntity.getPropertyAccessor(entity)::getProperty)
.map(r -> converter.convertIfNecessary(r, String.class))
.ifPresent(opts::ifMatch);
try {
arangoTemplate.delete(id, opts, domainClass);
} catch (DocumentNotFoundException e) {
throw new OptimisticLockingFailureException(e.getMessage(), e);
}
}
/**
* Deletes all instances of the type {@code T} with the given IDs.
* @implNote do not add @Override annotation to keep backwards compatibility with spring-data-commons 2.4
*/
public void deleteAllById(Iterable extends ID> ids) {
MultiDocumentEntity> res = arangoTemplate.deleteAllById(ids, domainClass);
for (ErrorEntity error : res.getErrors()) {
// Entities that aren't found in the persistence store are silently ignored.
if (error.getErrorNum() != 1202) {
throw arangoTemplate.translateException(new ArangoDBException(error));
}
}
}
/**
* Deletes all the given documents from the database
*
* @param entities iterable of entities to be deleted from the database
*/
@Override
public void deleteAll(final Iterable extends T> entities) {
entities.forEach(this::delete);
}
/**
* Deletes all documents in the collection for this repository
*/
@Override
public void deleteAll() {
arangoTemplate.collection(domainClass).truncate();
}
/**
* Gets all documents in the collection for the class type of this repository,
* with the given sort applied
*
* @param sort the sort object to use for sorting
* @return an iterable with all the documents in the collection
*/
@Override
public Iterable findAll(final Sort sort) {
return new Iterable() {
@Override
public Iterator iterator() {
return findAllInternal(sort, null, new HashMap<>());
}
};
}
/**
* Gets all documents in the collection for the class type of this repository,
* with pagination
*
* @param pageable the pageable object to use for pagination of the results
* @return an iterable with all the documents in the collection
*/
@Override
public Page findAll(final Pageable pageable) {
if (pageable == null) {
LOGGER.debug("Pageable in findAll(Pageable) is null");
}
final ArangoCursor result = findAllInternal(pageable, null, new HashMap<>());
final List content = result.asListRemaining();
return new PageImpl<>(content, pageable, ((Number) result.getStats().getFullCount()).longValue());
}
/**
* Gets the name of the collection for this repository
*
* @return the name of the collection
*/
private String getCollectionName() {
return persistentEntity.getCollection();
}
/**
* Finds one document which matches the given example object
*
* @param example example object to construct query with
* @param
* @return An object representing the example if it exists, else null
*/
@Override
public Optional findOne(final Example example) {
final ArangoCursor cursor = findAllInternal((Pageable) null, example, new HashMap());
return cursor.hasNext() ? Optional.ofNullable((S) cursor.next()) : Optional.empty();
}
/**
* Finds all documents which match with the given example
*
* @param example example object to construct query with
* @param
* @return iterable of all matching documents
*/
@Override
public Iterable findAll(final Example example) {
return (ArangoCursor) findAllInternal((Pageable) null, example, new HashMap<>());
}
/**
* Finds all documents which match with the given example, then apply the given
* sort to results
*
* @param example example object to construct query with
* @param sort sort object to sort results
* @param
* @return sorted iterable of all matching documents
*/
@Override
public Iterable findAll(final Example example, final Sort sort) {
return findAllInternal(sort, example, new HashMap());
}
/**
* Finds all documents which match with the given example, with pagination
*
* @param example example object to construct query with
* @param pageable pageable object to apply pagination with
* @param
* @return iterable of all matching documents, with pagination
*/
@Override
public Page findAll(final Example example, final Pageable pageable) {
final ArangoCursor cursor = findAllInternal(pageable, example, new HashMap());
final List content = cursor.asListRemaining();
return new PageImpl<>((List) content, pageable, ((Number) cursor.getStats().getFullCount()).longValue());
}
/**
* Counts the number of documents in the collection which match with the given
* example
*
* @param example example object to construct query with
* @param
* @return number of matching documents found
*/
@Override
public long count(final Example example) {
final Map bindVars = new HashMap<>();
bindVars.put("@col", getCollectionName());
final String predicate = exampleConverter.convertExampleToPredicate(example, bindVars);
final String filter = predicate.length() == 0 ? "" : " FILTER " + predicate;
final String query = String.format("FOR e IN @@col %s COLLECT WITH COUNT INTO length RETURN length", filter);
arangoTemplate.collection(domainClass);
final ArangoCursor cursor = arangoTemplate.query(query, bindVars, null, Long.class);
return cursor.next();
}
/**
* Checks if any documents match with the given example
*
* @param example
* @param
* @return true if any matches are found, else false
*/
@Override
public boolean exists(final Example example) {
return count(example) > 0;
}
public R findBy(Example example, Function, R> queryFunction) {
throw new UnsupportedOperationException();
}
private ArangoCursor findAllInternal(final Sort sort, @Nullable final Example example,
final Map bindVars) {
bindVars.put("@col", getCollectionName());
final String query = String.format("FOR e IN @@col %s %s RETURN e",
buildFilterClause(example, bindVars), buildSortClause(sort, "e"));
arangoTemplate.collection(domainClass);
return arangoTemplate.query(query, bindVars, null, domainClass);
}
private ArangoCursor findAllInternal(final Pageable pageable, @Nullable final Example example,
final Map bindVars) {
bindVars.put("@col", getCollectionName());
final String query = String.format("FOR e IN @@col %s %s RETURN e",
buildFilterClause(example, bindVars), buildPageableClause(pageable, "e"));
arangoTemplate.collection(domainClass);
return arangoTemplate.query(query, bindVars,
pageable != null ? new AqlQueryOptions().fullCount(true) : null, domainClass);
}
private String buildFilterClause(final Example example, final Map bindVars) {
if (example == null) {
return "";
}
final String predicate = exampleConverter.convertExampleToPredicate(example, bindVars);
return predicate == null ? "" : "FILTER " + predicate;
}
private String buildPageableClause(final Pageable pageable, final String varName) {
if (pageable == null) return "";
Sort persistentSort = AqlUtils.toPersistentSort(pageable.getSort(), mappingContext, domainClass);
Pageable persistentPageable;
if (pageable.isPaged()) {
persistentPageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), persistentSort);
} else {
persistentPageable = pageable;
}
return AqlUtils.buildPageableClause(persistentPageable, varName);
}
private String buildSortClause(final Sort sort, final String varName) {
return sort == null ? "" : AqlUtils.buildSortClause(AqlUtils.toPersistentSort(sort, mappingContext, domainClass), varName);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy