/*
* 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);
}
}