/* * Copyright 2015-2016 OpenCB * * 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. */ package org.opencb.opencga.catalog.db.mongodb; import com.mongodb.MongoClient; import com.mongodb.client.MongoCursor; import com.mongodb.client.model.Aggregates; import com.mongodb.client.model.Filters; import com.mongodb.client.model.Projections; import com.mongodb.client.model.Updates; import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.UpdateResult; import org.bson.Document; import org.bson.conversions.Bson; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.commons.datastore.core.QueryResult; import org.opencb.commons.datastore.mongodb.GenericDocumentComplexConverter; import org.opencb.commons.datastore.mongodb.MongoDBCollection; import org.opencb.opencga.catalog.db.api.*; import org.opencb.opencga.catalog.db.mongodb.converters.SampleConverter; import org.opencb.opencga.catalog.exceptions.CatalogDBException; import org.opencb.opencga.catalog.models.*; import org.opencb.opencga.catalog.models.acls.permissions.SampleAclEntry; import org.opencb.opencga.core.common.TimeUtils; import org.slf4j.LoggerFactory; import javax.annotation.Nullable; import java.util.*; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; import static org.opencb.opencga.catalog.db.mongodb.MongoDBUtils.*; import static org.opencb.opencga.catalog.utils.CatalogMemberValidator.checkMembers; /** * Created by hpccoll1 on 14/08/15. */ public class SampleMongoDBAdaptor extends AnnotationMongoDBAdaptor implements SampleDBAdaptor { private final MongoDBCollection sampleCollection; private SampleConverter sampleConverter; private AclMongoDBAdaptor<SampleAclEntry> aclDBAdaptor; public SampleMongoDBAdaptor(MongoDBCollection sampleCollection, MongoDBAdaptorFactory dbAdaptorFactory) { super(LoggerFactory.getLogger(SampleMongoDBAdaptor.class)); this.dbAdaptorFactory = dbAdaptorFactory; this.sampleCollection = sampleCollection; this.sampleConverter = new SampleConverter(); this.aclDBAdaptor = new AclMongoDBAdaptor<>(sampleCollection, sampleConverter, logger); } @Override protected GenericDocumentComplexConverter<? extends Annotable> getConverter() { return sampleConverter; } @Override protected MongoDBCollection getCollection() { return sampleCollection; } /* * Samples methods * *************************** */ @Override public QueryResult<Sample> insert(Sample sample, long studyId, QueryOptions options) throws CatalogDBException { long startTime = startQuery(); dbAdaptorFactory.getCatalogStudyDBAdaptor().checkId(studyId); /* QueryResult<Long> count = sampleCollection.count( new BasicDBObject("name", sample.getName()).append(PRIVATE_STUDY_ID, studyId)); */ List<Bson> filterList = new ArrayList<>(); filterList.add(Filters.eq(QueryParams.NAME.key(), sample.getName())); filterList.add(Filters.eq(PRIVATE_STUDY_ID, studyId)); filterList.add(Filters.eq(QueryParams.STATUS_NAME.key(), Status.READY)); Bson bson = Filters.and(filterList); QueryResult<Long> count = sampleCollection.count(bson); // new BsonDocument("name", new BsonString(sample.getName())).append(PRIVATE_STUDY_ID, new BsonInt32(studyId))); if (count.getResult().get(0) > 0) { throw new CatalogDBException("Sample { name: '" + sample.getName() + "'} already exists."); } long sampleId = getNewId(); sample.setId(sampleId); sample.setAnnotationSets(Collections.<AnnotationSet>emptyList()); //TODO: Add annotationSets Document sampleObject = sampleConverter.convertToStorageType(sample); sampleObject.put(PRIVATE_STUDY_ID, studyId); sampleObject.put(PRIVATE_ID, sampleId); sampleCollection.insert(sampleObject, null); return endQuery("createSample", startTime, get(sampleId, options)); } @Override public QueryResult<Sample> get(long sampleId, QueryOptions options) throws CatalogDBException { checkId(sampleId); return get(new Query(QueryParams.ID.key(), sampleId).append(QueryParams.STATUS_NAME.key(), "!=" + Status.DELETED), options); // long startTime = startQuery(); // //QueryOptions filteredOptions = filterOptions(options, FILTER_ROUTE_SAMPLES); // //// DBObject query = new BasicDBObject(PRIVATE_ID, sampleId); // Query query1 = new Query(QueryParams.ID.key(), sampleId); // QueryResult<Sample> sampleQueryResult = get(query1, options); // //// QueryResult<Document> queryResult = sampleCollection.find(bson, filteredOptions); //// List<Sample> samples = parseSamples(queryResult); // // if (sampleQueryResult.getResult().size() == 0) { // throw CatalogDBException.idNotFound("Sample", sampleId); // } // // return endQuery("getSample", startTime, sampleQueryResult); } // @Override // public QueryResult<Sample> getAllSamples(QueryOptions options) throws CatalogDBException { // throw new UnsupportedOperationException("Deprecated method. Use get instead"); // /* // int variableSetId = options.getInt(SampleFilterOption.variableSetId.toString()); // Map<String, Variable> variableMap = null; // if (variableSetId > 0) { // variableMap = dbAdaptorFactory.getCatalogStudyDBAdaptor().getVariableSet(variableSetId, null).first() // .getVariables().stream().collect(Collectors.toMap(Variable::getId, Function.identity())); // } // return getAllSamples(variableMap, options);*/ // } @Override public QueryResult<Sample> getAllInStudy(long studyId, QueryOptions options) throws CatalogDBException { long startTime = startQuery(); Query query = new Query(QueryParams.STUDY_ID.key(), studyId); return endQuery("Get all files", startTime, get(query, options).getResult()); } @Override public QueryResult<Sample> update(long sampleId, QueryOptions parameters) throws CatalogDBException { long startTime = startQuery(); Map<String, Object> sampleParams = new HashMap<>(); //List<Bson> sampleParams = new ArrayList<>(); String[] acceptedParams = {QueryParams.SOURCE.key(), QueryParams.DESCRIPTION.key(), QueryParams.NAME.key()}; filterStringParams(parameters, sampleParams, acceptedParams); if (sampleParams.containsKey(QueryParams.NAME.key())) { // Check that the new sample name is still unique long studyId = getStudyId(sampleId); QueryResult<Long> count = sampleCollection.count( new Document(QueryParams.NAME.key(), sampleParams.get(QueryParams.NAME.key())).append(PRIVATE_STUDY_ID, studyId)); if (count.getResult().get(0) > 0) { throw new CatalogDBException("Sample { name: '" + sampleParams.get(QueryParams.NAME.key()) + "'} already exists."); } } String[] acceptedLongParams = {QueryParams.INDIVIDUAL_ID.key()}; filterLongParams(parameters, sampleParams, acceptedLongParams); String[] acceptedMapParams = {QueryParams.ATTRIBUTES.key()}; filterMapParams(parameters, sampleParams, acceptedMapParams); // if (sampleParams.containsKey(QueryParams.INDIVIDUAL_ID.key())) { // long individualId = parameters.getInt(QueryParams.INDIVIDUAL_ID.key()); // if (individualId > 0 && !dbAdaptorFactory.getCatalogIndividualDBAdaptor().exists(individualId)) { // throw CatalogDBException.idNotFound("Individual", individualId); // } // } if (!sampleParams.isEmpty()) { /*QueryResult<WriteResult> update = sampleCollection.update(new BasicDBObject(PRIVATE_ID, sampleId), new BasicDBObject("$set", sampleParams), null); */ Bson query = Filters.eq(PRIVATE_ID, sampleId); Bson operation = new Document("$set", sampleParams); QueryResult<UpdateResult> update = sampleCollection.update(query, operation, null); if (update.getResult().isEmpty() || update.getResult().get(0).getMatchedCount() == 0) { throw CatalogDBException.idNotFound("Sample", sampleId); } } return endQuery("Modify sample", startTime, get(sampleId, new QueryOptions())); } public void unsetSampleAcl(long sampleId, List<String> members, List<String> permissions) throws CatalogDBException { // Check that all the members (users) are correct and exist. checkMembers(dbAdaptorFactory, getStudyId(sampleId), members); // Remove the permissions the members might have had for (String member : members) { Document query = new Document(PRIVATE_ID, sampleId).append(QueryParams.ACL_MEMBER.key(), member); Bson update; if (permissions.size() == 0) { update = new Document("$pull", new Document("acl", new Document("member", member))); } else { update = new Document("$pull", new Document("acl.$.permissions", new Document("$in", permissions))); } QueryResult<UpdateResult> updateResult = sampleCollection.update(query, update, null); if (updateResult.first().getModifiedCount() == 0) { throw new CatalogDBException("unsetSampleAcl: An error occurred when trying to stop sharing sample " + sampleId + " with other " + member + "."); } } // Remove possible SampleAcls that might have permissions defined but no users // Bson queryBson = new Document(QueryParams.ID.key(), sampleId) // .append(QueryParams.ACL_MEMBER.key(), // new Document("$exists", true).append("$eq", Collections.emptyList())); // Bson update = new Document("$pull", new Document("acls", new Document("users", Collections.emptyList()))); // sampleCollection.update(queryBson, update, null); } @Override public long getStudyId(long sampleId) throws CatalogDBException { /*DBObject query = new BasicDBObject(PRIVATE_ID, sampleId); BasicDBObject projection = new BasicDBObject(PRIVATE_STUDY_ID, true); QueryResult<DBObject> queryResult = sampleCollection.find(query, projection, null);*/ Bson query = new Document(PRIVATE_ID, sampleId); Bson projection = Projections.include(PRIVATE_STUDY_ID); QueryResult<Document> queryResult = sampleCollection.find(query, projection, null); if (!queryResult.getResult().isEmpty()) { Object studyId = queryResult.getResult().get(0).get(PRIVATE_STUDY_ID); return studyId instanceof Number ? ((Number) studyId).longValue() : Long.parseLong(studyId.toString()); } else { throw CatalogDBException.idNotFound("Sample", sampleId); } } @Override public List<Long> getStudyIdsBySampleIds(String sampleIds) throws CatalogDBException { Bson query = parseQuery(new Query(QueryParams.ID.key(), sampleIds), false); return sampleCollection.distinct(PRIVATE_STUDY_ID, query, Long.class).getResult(); } /* * Annotations Methods * *************************** */ @Override @Deprecated public QueryResult<AnnotationSet> annotate(long sampleId, AnnotationSet annotationSet, boolean overwrite) throws CatalogDBException { long startTime = startQuery(); /*QueryResult<Long> count = sampleCollection.count( new BasicDBObject("annotationSets.id", annotationSet.getId()).append(PRIVATE_ID, sampleId));*/ QueryResult<Long> count = sampleCollection.count(new Document("annotationSets.name", annotationSet.getName()) .append(PRIVATE_ID, sampleId)); if (overwrite) { if (count.getResult().get(0) == 0) { throw CatalogDBException.idNotFound("AnnotationSet", annotationSet.getName()); } } else { if (count.getResult().get(0) > 0) { throw CatalogDBException.alreadyExists("AnnotationSet", "name", annotationSet.getName()); } } /*DBObject object = getDbObject(annotationSet, "AnnotationSet"); DBObject query = new BasicDBObject(PRIVATE_ID, sampleId);*/ Document object = getMongoDBDocument(annotationSet, "AnnotationSet"); Bson query = new Document(PRIVATE_ID, sampleId); if (overwrite) { ((Document) query).put("annotationSets.name", annotationSet.getName()); } else { ((Document) query).put("annotationSets.name", new Document("$ne", annotationSet.getName())); } /* DBObject update; if (overwrite) { update = new BasicDBObject("$set", new BasicDBObject("annotationSets.$", object)); } else { update = new BasicDBObject("$push", new BasicDBObject("annotationSets", object)); } */ Bson update; if (overwrite) { update = Updates.set("annotationSets.$", object); } else { update = Updates.push("annotationSets", object); } QueryResult<UpdateResult> queryResult = sampleCollection.update(query, update, null); if (queryResult.first().getModifiedCount() != 1) { throw CatalogDBException.alreadyExists("AnnotationSet", "name", annotationSet.getName()); } return endQuery("", startTime, Collections.singletonList(annotationSet)); } @Override @Deprecated public QueryResult<AnnotationSet> deleteAnnotation(long sampleId, String annotationId) throws CatalogDBException { long startTime = startQuery(); Sample sample = get(sampleId, new QueryOptions("include", "projects.studies.samples.annotationSets")).first(); AnnotationSet annotationSet = null; for (AnnotationSet as : sample.getAnnotationSets()) { if (as.getName().equals(annotationId)) { annotationSet = as; break; } } if (annotationSet == null) { throw CatalogDBException.idNotFound("AnnotationSet", annotationId); } /* DBObject query = new BasicDBObject(PRIVATE_ID, sampleId); DBObject update = new BasicDBObject("$pull", new BasicDBObject("annotationSets", new BasicDBObject("id", annotationId))); */ Bson query = new Document(PRIVATE_ID, sampleId); Bson update = Updates.pull("annotationSets", new Document("name", annotationId)); QueryResult<UpdateResult> resultQueryResult = sampleCollection.update(query, update, null); if (resultQueryResult.first().getModifiedCount() < 1) { throw CatalogDBException.idNotFound("AnnotationSet", annotationId); } return endQuery("Delete annotation", startTime, Collections.singletonList(annotationSet)); } @Deprecated public void checkInUse(long sampleId) throws CatalogDBException { long studyId = getStudyId(sampleId); Query query = new Query(FileDBAdaptor.QueryParams.SAMPLE_IDS.key(), sampleId); QueryOptions queryOptions = new QueryOptions(MongoDBCollection.INCLUDE, Arrays.asList(FILTER_ROUTE_FILES + FileDBAdaptor .QueryParams.ID.key(), FILTER_ROUTE_FILES + FileDBAdaptor.QueryParams.PATH.key())); QueryResult<File> fileQueryResult = dbAdaptorFactory.getCatalogFileDBAdaptor().get(query, queryOptions); if (fileQueryResult.getNumResults() != 0) { String msg = "Can't delete Sample " + sampleId + ", still in use in \"sampleId\" array of files : " + fileQueryResult.getResult().stream() .map(file -> "{ id: " + file.getId() + ", path: \"" + file.getPath() + "\" }") .collect(Collectors.joining(", ", "[", "]")); throw new CatalogDBException(msg); } queryOptions = new QueryOptions(CohortDBAdaptor.QueryParams.SAMPLES.key(), sampleId) .append(MongoDBCollection.INCLUDE, Arrays.asList(FILTER_ROUTE_COHORTS + CohortDBAdaptor.QueryParams.ID.key(), FILTER_ROUTE_COHORTS + CohortDBAdaptor.QueryParams.NAME.key())); QueryResult<Cohort> cohortQueryResult = dbAdaptorFactory.getCatalogCohortDBAdaptor().getAllInStudy(studyId, queryOptions); if (cohortQueryResult.getNumResults() != 0) { String msg = "Can't delete Sample " + sampleId + ", still in use in cohorts : " + cohortQueryResult.getResult().stream() .map(cohort -> "{ id: " + cohort.getId() + ", name: \"" + cohort.getName() + "\" }") .collect(Collectors.joining(", ", "[", "]")); throw new CatalogDBException(msg); } } /** * To be able to delete a sample, the sample does not have to be part of any cohort. * * @param sampleId sample id. * @throws CatalogDBException if the sampleId is used on any cohort. */ private void checkCanDelete(long sampleId) throws CatalogDBException { Query query = new Query(CohortDBAdaptor.QueryParams.SAMPLES.key(), sampleId); if (dbAdaptorFactory.getCatalogCohortDBAdaptor().count(query).first() > 0) { List<Cohort> cohorts = dbAdaptorFactory.getCatalogCohortDBAdaptor() .get(query, new QueryOptions(QueryOptions.INCLUDE, CohortDBAdaptor.QueryParams.ID.key())).getResult(); throw new CatalogDBException("The sample {" + sampleId + "} cannot be deleted/removed. It is being used in " + cohorts.size() + " cohorts: [" + cohorts.stream().map(Cohort::getId).collect(Collectors.toList()).toString() + "]"); } } @Override public QueryResult<Long> count(Query query) throws CatalogDBException { Bson bson = parseQuery(query, false); return sampleCollection.count(bson); } @Override public QueryResult distinct(Query query, String field) throws CatalogDBException { Bson bson = parseQuery(query, false); return sampleCollection.distinct(field, bson); } @Override public QueryResult stats(Query query) { return null; } @Override public QueryResult<Sample> get(Query query, QueryOptions options) throws CatalogDBException { long startTime = startQuery(); if (!query.containsKey(QueryParams.STATUS_NAME.key())) { query.append(QueryParams.STATUS_NAME.key(), "!=" + Status.TRASHED + ";!=" + Status.DELETED); } Bson bson = parseQuery(query, false); QueryOptions qOptions; if (options != null) { qOptions = options; } else { qOptions = new QueryOptions(); } qOptions = filterOptions(qOptions, FILTER_ROUTE_SAMPLES); QueryResult<Sample> sampleQueryResult; if (qOptions.get("lazy") != null && !qOptions.getBoolean("lazy")) { Bson match = Aggregates.match(bson); Bson lookup = Aggregates.lookup("individual", QueryParams.INDIVIDUAL_ID.key(), IndividualDBAdaptor.QueryParams.ID.key(), "individual"); sampleQueryResult = sampleCollection.aggregate(Arrays.asList(match, lookup), sampleConverter, qOptions); } else { sampleQueryResult = sampleCollection.find(bson, sampleConverter, qOptions); } logger.debug("Sample get: query : {}, dbTime: {}", bson.toBsonDocument(Document.class, MongoClient.getDefaultCodecRegistry()), qOptions == null ? "" : qOptions.toJson(), sampleQueryResult.getDbTime()); return endQuery("Get sample", startTime, sampleQueryResult); } @Override public QueryResult nativeGet(Query query, QueryOptions options) throws CatalogDBException { if (!query.containsKey(QueryParams.STATUS_NAME.key())) { query.append(QueryParams.STATUS_NAME.key(), "!=" + Status.TRASHED + ";!=" + Status.DELETED); } Bson bson = parseQuery(query, false); QueryOptions qOptions; if (options != null) { qOptions = options; } else { qOptions = new QueryOptions(); } //qOptions.append(MongoDBCollection.EXCLUDE, Arrays.asList(PRIVATE_ID, PRIVATE_STUDY_ID)); qOptions = filterOptions(qOptions, FILTER_ROUTE_SAMPLES); return sampleCollection.find(bson, qOptions); } @Override public QueryResult<Long> update(Query query, ObjectMap parameters) throws CatalogDBException { long startTime = startQuery(); Map<String, Object> sampleParameters = new HashMap<>(); final String[] acceptedBooleanParams = {QueryParams.SOMATIC.key()}; filterBooleanParams(parameters, sampleParameters, acceptedBooleanParams); final String[] acceptedParams = {QueryParams.SOURCE.key(), QueryParams.DESCRIPTION.key()}; filterStringParams(parameters, sampleParameters, acceptedParams); final String[] acceptedIntParams = {QueryParams.ID.key(), QueryParams.INDIVIDUAL_ID.key()}; filterIntParams(parameters, sampleParameters, acceptedIntParams); final String[] acceptedMapParams = {QueryParams.ATTRIBUTES.key()}; filterMapParams(parameters, sampleParameters, acceptedMapParams); if (parameters.containsKey(QueryParams.NAME.key())) { // That can only be done to one sample... QueryResult<Sample> sampleQueryResult = get(query, new QueryOptions()); if (sampleQueryResult.getNumResults() == 0) { throw new CatalogDBException("Update sample: No sample found to be updated"); } if (sampleQueryResult.getNumResults() > 1) { throw new CatalogDBException("Update sample: Cannot update name parameter. More than one sample found to be updated."); } // Check that the new sample name is still unique long studyId = getStudyId(sampleQueryResult.first().getId()); Query tmpQuery = new Query() .append(QueryParams.NAME.key(), parameters.get(QueryParams.NAME.key())) .append(QueryParams.STUDY_ID.key(), studyId); QueryResult<Long> count = count(tmpQuery); if (count.getResult().get(0) > 0) { throw new CatalogDBException("Sample { name: '" + parameters.get(QueryParams.NAME.key()) + "'} already exists."); } sampleParameters.put(QueryParams.NAME.key(), parameters.get(QueryParams.NAME.key())); } if (parameters.containsKey(QueryParams.STATUS_NAME.key())) { sampleParameters.put(QueryParams.STATUS_NAME.key(), parameters.get(QueryParams.STATUS_NAME.key())); sampleParameters.put(QueryParams.STATUS_DATE.key(), TimeUtils.getTime()); } if (!sampleParameters.isEmpty()) { QueryResult<UpdateResult> update = sampleCollection.update(parseQuery(query, false), new Document("$set", sampleParameters), null); return endQuery("Update sample", startTime, Arrays.asList(update.getNumTotalResults())); } return endQuery("Update sample", startTime, new QueryResult<>()); } @Override public void delete(long id) throws CatalogDBException { Query query = new Query(QueryParams.ID.key(), id); delete(query); } @Override public void delete(Query query) throws CatalogDBException { QueryResult<DeleteResult> remove = sampleCollection.remove(parseQuery(query, false), null); if (remove.first().getDeletedCount() == 0) { throw CatalogDBException.deleteError("Sample"); } } @Override public QueryResult<Sample> update(long id, ObjectMap parameters) throws CatalogDBException { long startTime = startQuery(); QueryResult<Long> update = update(new Query(QueryParams.ID.key(), id), parameters); if (update.getNumTotalResults() != 1) { throw new CatalogDBException("Could not update sample with id " + id); } Query query = new Query() .append(QueryParams.ID.key(), id) .append(QueryParams.STATUS_NAME.key(), "!=EMPTY"); return endQuery("Update sample", startTime, get(query, new QueryOptions())); } // TODO: Check clean public QueryResult<Sample> clean(long id) throws CatalogDBException { throw new UnsupportedOperationException("Clean is not yet implemented."); } @Override public QueryResult<Long> restore(Query query, QueryOptions queryOptions) throws CatalogDBException { long startTime = startQuery(); query.put(QueryParams.STATUS_NAME.key(), Status.TRASHED); return endQuery("Restore samples", startTime, setStatus(query, Status.READY)); } @Override public QueryResult<Sample> restore(long id, QueryOptions queryOptions) throws CatalogDBException { long startTime = startQuery(); checkId(id); // Check if the cohort is active Query query = new Query(QueryParams.ID.key(), id) .append(QueryParams.STATUS_NAME.key(), Status.TRASHED); if (count(query).first() == 0) { throw new CatalogDBException("The sample {" + id + "} is not deleted"); } // Change the status of the cohort to deleted setStatus(id, Status.READY); query = new Query(QueryParams.ID.key(), id); return endQuery("Restore sample", startTime, get(query, null)); } /*** * Checks whether the sample id corresponds to any Individual and if it is parent of any other individual. * @param id Sample id that will be checked. * @throws CatalogDBException when the sample is parent of other individual. */ @Deprecated public void checkSampleIsParentOfFamily(int id) throws CatalogDBException { Sample sample = get(id, new QueryOptions()).first(); if (sample.getIndividual().getId() > 0) { Query query = new Query(IndividualDBAdaptor.QueryParams.FATHER_ID.key(), sample.getIndividual().getId()) .append(IndividualDBAdaptor.QueryParams.MOTHER_ID.key(), sample.getIndividual().getId()); Long count = dbAdaptorFactory.getCatalogIndividualDBAdaptor().count(query).first(); if (count > 0) { throw CatalogDBException.sampleIdIsParentOfOtherIndividual(id); } } } @Override public DBIterator<Sample> iterator(Query query, QueryOptions options) throws CatalogDBException { Bson bson = parseQuery(query, false); MongoCursor<Document> iterator = sampleCollection.nativeQuery().find(bson, options).iterator(); return new MongoDBIterator<>(iterator, sampleConverter); } @Override public DBIterator nativeIterator(Query query, QueryOptions options) throws CatalogDBException { Bson bson = parseQuery(query, false); MongoCursor<Document> iterator = sampleCollection.nativeQuery().find(bson, options).iterator(); return new MongoDBIterator<>(iterator); } @Override public QueryResult rank(Query query, String field, int numResults, boolean asc) throws CatalogDBException { Bson bsonQuery = parseQuery(query, false); return rank(sampleCollection, bsonQuery, field, "name", numResults, asc); } @Override public QueryResult groupBy(Query query, String field, QueryOptions options) throws CatalogDBException { Bson bsonQuery = parseQuery(query, false); return groupBy(sampleCollection, bsonQuery, field, "name", options); } @Override public QueryResult groupBy(Query query, List<String> fields, QueryOptions options) throws CatalogDBException { Bson bsonQuery = parseQuery(query, false); return groupBy(sampleCollection, bsonQuery, fields, "name", options); } @Override public void forEach(Query query, Consumer<? super Object> action, QueryOptions options) throws CatalogDBException { Objects.requireNonNull(action); DBIterator<Sample> catalogDBIterator = iterator(query, options); while (catalogDBIterator.hasNext()) { action.accept(catalogDBIterator.next()); } catalogDBIterator.close(); } private Bson parseQuery(Query query, boolean isolated) throws CatalogDBException { List<Bson> andBsonList = new ArrayList<>(); List<Bson> annotationList = new ArrayList<>(); // We declare variableMap here just in case we have different annotation queries Map<String, Variable> variableMap = null; if (isolated) { andBsonList.add(new Document("$isolated", 1)); } if (query.containsKey(QueryParams.ANNOTATION.key())) { fixAnnotationQuery(query); } for (Map.Entry<String, Object> entry : query.entrySet()) { String key = entry.getKey().split("\\.")[0]; QueryParams queryParam = QueryParams.getParam(entry.getKey()) != null ? QueryParams.getParam(entry.getKey()) : QueryParams.getParam(key); if (queryParam == null) { continue; } try { switch (queryParam) { case ID: addOrQuery(PRIVATE_ID, queryParam.key(), query, queryParam.type(), andBsonList); break; case STUDY_ID: addOrQuery(PRIVATE_STUDY_ID, queryParam.key(), query, queryParam.type(), andBsonList); break; case ATTRIBUTES: addAutoOrQuery(entry.getKey(), entry.getKey(), query, queryParam.type(), andBsonList); break; case BATTRIBUTES: String mongoKey = entry.getKey().replace(QueryParams.BATTRIBUTES.key(), QueryParams.ATTRIBUTES.key()); addAutoOrQuery(mongoKey, entry.getKey(), query, queryParam.type(), andBsonList); break; case NATTRIBUTES: mongoKey = entry.getKey().replace(QueryParams.NATTRIBUTES.key(), QueryParams.ATTRIBUTES.key()); addAutoOrQuery(mongoKey, entry.getKey(), query, queryParam.type(), andBsonList); break; case ONTOLOGIES: case ONTOLOGY_TERMS: addOntologyQueryFilter(QueryParams.ONTOLOGY_TERMS.key(), queryParam.key(), query, andBsonList); break; case VARIABLE_SET_ID: addOrQuery(queryParam.key(), queryParam.key(), query, queryParam.type(), annotationList); break; case ANNOTATION: if (variableMap == null) { long variableSetId = query.getLong(QueryParams.VARIABLE_SET_ID.key()); if (variableSetId > 0) { variableMap = dbAdaptorFactory.getCatalogStudyDBAdaptor().getVariableSet(variableSetId, null).first() .getVariables().stream().collect(Collectors.toMap(Variable::getName, Function.identity())); } } addAnnotationQueryFilter(entry.getKey(), query, variableMap, annotationList); break; case ANNOTATION_SET_NAME: addOrQuery("name", queryParam.key(), query, queryParam.type(), annotationList); break; case NAME: case SOURCE: case INDIVIDUAL_ID: case DESCRIPTION: case STATUS_NAME: case STATUS_MSG: case STATUS_DATE: case ACL: case ACL_MEMBER: case ACL_PERMISSIONS: case ONTOLOGY_TERMS_ID: case ONTOLOGY_TERMS_NAME: case ONTOLOGY_TERMS_SOURCE: case ONTOLOGY_TERMS_AGE_OF_ONSET: case ONTOLOGY_TERMS_MODIFIERS: case ANNOTATION_SETS: addAutoOrQuery(queryParam.key(), queryParam.key(), query, queryParam.type(), andBsonList); break; default: break; } } catch (Exception e) { if (e instanceof CatalogDBException) { throw e; } else { throw new CatalogDBException("Error parsing query : " + query.toJson(), e); } } } if (annotationList.size() > 0) { Bson projection = Projections.elemMatch(QueryParams.ANNOTATION_SETS.key(), Filters.and(annotationList)); andBsonList.add(projection); } if (andBsonList.size() > 0) { return Filters.and(andBsonList); } else { return new Document(); } } public MongoDBCollection getSampleCollection() { return sampleCollection; } QueryResult<Sample> setStatus(long sampleId, String status) throws CatalogDBException { return update(sampleId, new ObjectMap(QueryParams.STATUS_NAME.key(), status)); } QueryResult<Long> setStatus(Query query, String status) throws CatalogDBException { return update(query, new ObjectMap(QueryParams.STATUS_NAME.key(), status)); } private void deleteReferencesToSample(long sampleId) throws CatalogDBException { // Remove references from files Query query = new Query(FileDBAdaptor.QueryParams.SAMPLE_IDS.key(), sampleId); QueryResult<Long> result = dbAdaptorFactory.getCatalogFileDBAdaptor() .extractSampleFromFiles(query, Collections.singletonList(sampleId)); logger.debug("SampleId {} extracted from {} files", sampleId, result.first()); // Remove references from cohorts query = new Query(CohortDBAdaptor.QueryParams.SAMPLES.key(), sampleId); result = dbAdaptorFactory.getCatalogCohortDBAdaptor().extractSamplesFromCohorts(query, Collections.singletonList(sampleId)); logger.debug("SampleId {} extracted from {} cohorts", sampleId, result.first()); } @Override public QueryResult<SampleAclEntry> createAcl(long id, SampleAclEntry acl) throws CatalogDBException { long startTime = startQuery(); // CatalogMongoDBUtils.setAcl(id, acl, sampleCollection, "SampleAcl"); return endQuery("create sample Acl", startTime, Arrays.asList(aclDBAdaptor.createAcl(id, acl))); } @Override public void createAcl(Query query, List<SampleAclEntry> aclEntryList) throws CatalogDBException { Bson queryDocument = parseQuery(query, true); aclDBAdaptor.setAcl(queryDocument, aclEntryList); } @Override public QueryResult<SampleAclEntry> getAcl(long id, List<String> members) throws CatalogDBException { long startTime = startQuery(); // // List<SampleAclEntry> acl = null; // QueryResult<Document> aggregate = CatalogMongoDBUtils.getAcl(id, members, sampleCollection, logger); // Sample sample = sampleConverter.convertToDataModelType(aggregate.first()); // // if (sample != null) { // acl = sample.getAcl(); // } return endQuery("get sample Acl", startTime, aclDBAdaptor.getAcl(id, members)); } @Override public void removeAcl(long id, String member) throws CatalogDBException { // CatalogMongoDBUtils.removeAcl(id, member, sampleCollection); aclDBAdaptor.removeAcl(id, member); } @Override public QueryResult<SampleAclEntry> setAclsToMember(long id, String member, List<String> permissions) throws CatalogDBException { long startTime = startQuery(); // CatalogMongoDBUtils.setAclsToMember(id, member, permissions, sampleCollection); return endQuery("Set Acls to member", startTime, Arrays.asList(aclDBAdaptor.setAclsToMember(id, member, permissions))); } @Deprecated @Override public QueryResult<SampleAclEntry> addAclsToMember(long id, String member, List<String> permissions) throws CatalogDBException { long startTime = startQuery(); // CatalogMongoDBUtils.addAclsToMember(id, member, permissions, sampleCollection); return endQuery("Add Acls to member", startTime, Arrays.asList(aclDBAdaptor.addAclsToMember(id, member, permissions))); } @Override public void addAclsToMember(Query query, List<String> members, List<String> permissions) throws CatalogDBException { QueryResult<Sample> sampleQueryResult = get(query, new QueryOptions(QueryOptions.INCLUDE, QueryParams.ID.key())); List<Long> sampleIds = sampleQueryResult.getResult().stream().map(sample -> sample.getId()).collect(Collectors.toList()); if (sampleIds == null || sampleIds.size() == 0) { throw new CatalogDBException("No matches found for query when attempting to add new permissions"); } aclDBAdaptor.addAclsToMembers(sampleIds, members, permissions); } @Deprecated @Override public QueryResult<SampleAclEntry> removeAclsFromMember(long id, String member, List<String> permissions) throws CatalogDBException { // CatalogMongoDBUtils.removeAclsFromMember(id, member, permissions, sampleCollection); long startTime = startQuery(); return endQuery("Remove Acls from member", startTime, Arrays.asList(aclDBAdaptor.removeAclsFromMember(id, member, permissions))); } @Override public void removeAclsFromMember(Query query, List<String> members, @Nullable List<String> permissions) throws CatalogDBException { QueryResult<Sample> sampleQueryResult = get(query, new QueryOptions(QueryOptions.INCLUDE, QueryParams.ID.key())); List<Long> sampleIds = sampleQueryResult.getResult().stream().map(Sample::getId).collect(Collectors.toList()); if (sampleIds == null || sampleIds.size() == 0) { throw new CatalogDBException("No matches found for query when attempting to remove permissions"); } aclDBAdaptor.removeAclsFromMembers(sampleIds, members, permissions); } public void removeAclsFromStudy(long studyId, String member) throws CatalogDBException { aclDBAdaptor.removeAclsFromStudy(studyId, member); } }