/* * 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.DuplicateKeyException; import com.mongodb.client.MongoCursor; import com.mongodb.client.model.Filters; 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.MongoDBCollection; import org.opencb.opencga.catalog.db.api.DBIterator; import org.opencb.opencga.catalog.db.api.PanelDBAdaptor; import org.opencb.opencga.catalog.db.mongodb.converters.PanelConverter; import org.opencb.opencga.catalog.exceptions.CatalogDBException; import org.opencb.opencga.catalog.models.DiseasePanel; import org.opencb.opencga.catalog.models.Status; import org.opencb.opencga.catalog.models.acls.permissions.DiseasePanelAclEntry; 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.stream.Collectors; import static org.opencb.opencga.catalog.db.mongodb.MongoDBUtils.filterOptions; import static org.opencb.opencga.catalog.db.mongodb.MongoDBUtils.filterStringParams; /** * Created by pfurio on 01/06/16. */ public class PanelMongoDBAdaptor extends MongoDBAdaptor implements PanelDBAdaptor { private final MongoDBCollection panelCollection; private PanelConverter panelConverter; private AclMongoDBAdaptor<DiseasePanelAclEntry> aclDBAdaptor; public PanelMongoDBAdaptor(MongoDBCollection panelCollection, MongoDBAdaptorFactory dbAdaptorFactory) { super(LoggerFactory.getLogger(JobMongoDBAdaptor.class)); this.dbAdaptorFactory = dbAdaptorFactory; this.panelCollection = panelCollection; this.panelConverter = new PanelConverter(); this.aclDBAdaptor = new AclMongoDBAdaptor<>(panelCollection, panelConverter, logger); } @Override public QueryResult<DiseasePanel> get(long diseasePanelId, QueryOptions options) throws CatalogDBException { checkId(diseasePanelId); Query query = new Query(QueryParams.ID.key(), diseasePanelId); return get(query, options); } @Override public long getStudyId(long panelId) throws CatalogDBException { checkId(panelId); QueryResult queryResult = nativeGet(new Query(QueryParams.ID.key(), panelId), new QueryOptions(QueryOptions.INCLUDE, PRIVATE_STUDY_ID)); if (queryResult.getResult().isEmpty()) { throw CatalogDBException.idNotFound("Panel", panelId); } else { return ((Document) queryResult.first()).getLong(PRIVATE_STUDY_ID); } } @Override public QueryResult<DiseasePanel> insert(DiseasePanel diseasePanel, long studyId, QueryOptions options) throws CatalogDBException { long startTime = startQuery(); dbAdaptorFactory.getCatalogStudyDBAdaptor().checkId(studyId); //new Panel Id long newPanelId = getNewId(); diseasePanel.setId(newPanelId); Document panelDocument = panelConverter.convertToStorageType(diseasePanel); panelDocument.append(PRIVATE_STUDY_ID, studyId); panelDocument.append(PRIVATE_ID, newPanelId); try { panelCollection.insert(panelDocument, null); } catch (DuplicateKeyException e) { throw CatalogDBException.alreadyExists("Panel", studyId, "name", diseasePanel.getName()); } return endQuery("Create panel", startTime, get(newPanelId, options)); } @Override public QueryResult<Long> count(Query query) throws CatalogDBException { return panelCollection.count(parseQuery(query, false)); } @Override public QueryResult distinct(Query query, String field) throws CatalogDBException { return panelCollection.distinct(field, parseQuery(query, false)); } @Override public QueryResult stats(Query query) { return null; } @Override public QueryResult<DiseasePanel> 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; try { bson = parseQuery(query, false); } catch (NumberFormatException e) { throw new CatalogDBException("Get panel: Could not parse all the arguments from query - " + e.getMessage(), e.getCause()); } QueryOptions qOptions; if (options != null) { qOptions = options; } else { qOptions = new QueryOptions(); } qOptions = filterOptions(qOptions, FILTER_ROUTE_PANELS); QueryResult<DiseasePanel> panelQueryResult = panelCollection.find(bson, panelConverter, qOptions); logger.debug("Panel get: query : {}, project: {}, dbTime: {}", bson, qOptions == null ? "" : qOptions.toJson(), panelQueryResult.getDbTime()); return endQuery("get Panel", startTime, panelQueryResult); } @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; try { bson = parseQuery(query, false); } catch (NumberFormatException e) { throw new CatalogDBException("Get panel: Could not parse all the arguments from query - " + e.getMessage(), e.getCause()); } QueryOptions qOptions; if (options != null) { qOptions = options; } else { qOptions = new QueryOptions(); } qOptions = filterOptions(qOptions, FILTER_ROUTE_PANELS); return panelCollection.find(bson, qOptions); } @Override public QueryResult<DiseasePanel> 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 panel with id " + id); } return endQuery("Update panel", startTime, get(id, null)); } @Override public QueryResult<Long> update(Query query, ObjectMap parameters) throws CatalogDBException { long startTime = startQuery(); Document updateOperations = new Document(); Map<String, Object> panelSetParameters = new HashMap<>(); // We read the string parameters // final String[] acceptedParams = {QueryParams.NAME.key(), QueryParams.DISEASE.key(), QueryParams.DESCRIPTION.key()}; final String[] acceptedParams = {QueryParams.DESCRIPTION.key()}; filterStringParams(parameters, panelSetParameters, acceptedParams); if (parameters.containsKey(QueryParams.STATUS_NAME.key())) { panelSetParameters.put(QueryParams.STATUS_NAME.key(), parameters.get(QueryParams.STATUS_NAME.key())); panelSetParameters.put(QueryParams.STATUS_DATE.key(), TimeUtils.getTimeMillis()); } // Create the update with set if (!panelSetParameters.isEmpty()) { updateOperations.put("$set", panelSetParameters); } // We read the list parameters (variants, genes & regions) Document panelAddToSetParameters = new Document(); if (parameters.containsKey(QueryParams.GENES.key())) { List<String> genes = parameters.getAsStringList(QueryParams.GENES.key()); if (genes.size() > 0) { panelAddToSetParameters.put(QueryParams.GENES.key(), new Document("$each", genes)); } } if (parameters.containsKey(QueryParams.REGIONS.key())) { List<String> regions = parameters.getAsStringList(QueryParams.REGIONS.key()); if (regions.size() > 0) { panelAddToSetParameters.put(QueryParams.REGIONS.key(), new Document("$each", regions)); } } if (parameters.containsKey(QueryParams.VARIANTS.key())) { List<String> variants = parameters.getAsStringList(QueryParams.VARIANTS.key()); if (variants.size() > 0) { panelAddToSetParameters.put(QueryParams.VARIANTS.key(), new Document("$each", variants)); } } // Create the update with addToSet if (panelAddToSetParameters.size() > 0) { updateOperations.put("$addToSet", panelAddToSetParameters); } if (updateOperations.size() > 0) { QueryResult<UpdateResult> update = panelCollection.update(parseQuery(query, false), updateOperations, null); return endQuery("Update panel", 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 = panelCollection.remove(parseQuery(query, false), null); if (remove.first().getDeletedCount() == 0) { throw CatalogDBException.deleteError("Disease panel"); } } @Override public QueryResult<DiseasePanel> delete(long id, QueryOptions queryOptions) throws CatalogDBException { throw new UnsupportedOperationException("Delete not yet implemented."); } @Override public QueryResult<Long> delete(Query query, QueryOptions queryOptions) throws CatalogDBException { throw new UnsupportedOperationException("Delete not yet implemented."); } @Override public QueryResult<DiseasePanel> remove(long id, QueryOptions queryOptions) throws CatalogDBException { throw new UnsupportedOperationException("Remove not yet implemented."); } @Override public QueryResult<Long> remove(Query query, QueryOptions queryOptions) throws CatalogDBException { throw new UnsupportedOperationException("Remove not yet implemented."); } @Override public QueryResult<DiseasePanel> restore(long id, QueryOptions queryOptions) throws CatalogDBException { throw new UnsupportedOperationException("Restore not yet implemented."); } @Override public QueryResult<Long> restore(Query query, QueryOptions queryOptions) throws CatalogDBException { throw new UnsupportedOperationException("Restore not yet implemented."); } @Override public DBIterator<DiseasePanel> iterator(Query query, QueryOptions options) throws CatalogDBException { Bson bson = parseQuery(query, false); MongoCursor<Document> iterator = panelCollection.nativeQuery().find(bson, options).iterator(); return new MongoDBIterator<>(iterator, panelConverter); } @Override public DBIterator nativeIterator(Query query, QueryOptions options) throws CatalogDBException { Bson bson = parseQuery(query, false); MongoCursor<Document> iterator = panelCollection.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(panelCollection, bsonQuery, field, "name", numResults, asc); } @Override public QueryResult groupBy(Query query, String field, QueryOptions options) throws CatalogDBException { Bson bsonQuery = parseQuery(query, false); return groupBy(panelCollection, bsonQuery, field, "name", options); } @Override public QueryResult groupBy(Query query, List<String> fields, QueryOptions options) throws CatalogDBException { Bson bsonQuery = parseQuery(query, false); return groupBy(panelCollection, bsonQuery, fields, "name", options); } @Override public void forEach(Query query, Consumer<? super Object> action, QueryOptions options) throws CatalogDBException { Objects.requireNonNull(action); DBIterator<DiseasePanel> 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<>(); if (isolated) { andBsonList.add(new Document("$isolated", 1)); } 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); 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; default: addAutoOrQuery(queryParam.key(), queryParam.key(), query, queryParam.type(), andBsonList); break; } } catch (Exception e) { logger.error("Error with " + entry.getKey() + " " + entry.getValue()); throw new CatalogDBException(e); } } if (andBsonList.size() > 0) { return Filters.and(andBsonList); } else { return new Document(); } } @Override public QueryResult<DiseasePanelAclEntry> createAcl(long id, DiseasePanelAclEntry acl) throws CatalogDBException { long startTime = startQuery(); // CatalogMongoDBUtils.setAcl(id, acl, panelCollection, "DiseasePanelAcl"); return endQuery("create panel Acl", startTime, Arrays.asList(aclDBAdaptor.createAcl(id, acl))); } @Override public void createAcl(Query query, List<DiseasePanelAclEntry> aclEntryList) throws CatalogDBException { Bson queryDocument = parseQuery(query, true); aclDBAdaptor.setAcl(queryDocument, aclEntryList); } @Override public QueryResult<DiseasePanelAclEntry> getAcl(long id, List<String> members) throws CatalogDBException { long startTime = startQuery(); // // List<DiseasePanelAclEntry> acl = null; // QueryResult<Document> aggregate = CatalogMongoDBUtils.getAcl(id, members, panelCollection, logger); // DiseasePanel panel = panelConverter.convertToDataModelType(aggregate.first()); // // if (panel != null) { // acl = panel.getAcl(); // } return endQuery("get panel Acl", startTime, aclDBAdaptor.getAcl(id, members)); } @Override public void removeAcl(long id, String member) throws CatalogDBException { // CatalogMongoDBUtils.removeAcl(id, member, panelCollection); aclDBAdaptor.removeAcl(id, member); } @Override public QueryResult<DiseasePanelAclEntry> setAclsToMember(long id, String member, List<String> permissions) throws CatalogDBException { long startTime = startQuery(); // CatalogMongoDBUtils.setAclsToMember(id, member, permissions, panelCollection); return endQuery("Set Acls to member", startTime, Arrays.asList(aclDBAdaptor.setAclsToMember(id, member, permissions))); } @Override public QueryResult<DiseasePanelAclEntry> addAclsToMember(long id, String member, List<String> permissions) throws CatalogDBException { long startTime = startQuery(); // CatalogMongoDBUtils.addAclsToMember(id, member, permissions, panelCollection); 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<DiseasePanel> panelQueryResult = get(query, new QueryOptions(QueryOptions.INCLUDE, QueryParams.ID.key())); List<Long> panelIds = panelQueryResult.getResult().stream().map(panel -> panel.getId()).collect(Collectors.toList()); if (panelIds == null || panelIds.size() == 0) { throw new CatalogDBException("No matches found for query when attempting to add new permissions"); } aclDBAdaptor.addAclsToMembers(panelIds, members, permissions); } @Override public QueryResult<DiseasePanelAclEntry> removeAclsFromMember(long id, String member, List<String> permissions) throws CatalogDBException { // CatalogMongoDBUtils.removeAclsFromMember(id, member, permissions, panelCollection); 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<DiseasePanel> panelQueryResult = get(query, new QueryOptions(QueryOptions.INCLUDE, QueryParams.ID.key())); List<Long> panelIds = panelQueryResult.getResult().stream().map(DiseasePanel::getId).collect(Collectors.toList()); if (panelIds == null || panelIds.size() == 0) { throw new CatalogDBException("No matches found for query when attempting to remove permissions"); } aclDBAdaptor.removeAclsFromMembers(panelIds, members, permissions); } public void removeAclsFromStudy(long studyId, String member) throws CatalogDBException { aclDBAdaptor.removeAclsFromStudy(studyId, member); } }