All Downloads are FREE. Search and download functionalities are using the official Maven repository.

step.core.collections.mongodb.MongoDBCollection Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (C) 2020, exense GmbH
 *  
 * This file is part of STEP
 *  
 * STEP is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *  
 * STEP is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *  
 * You should have received a copy of the GNU Affero General Public License
 * along with STEP.  If not, see .
 ******************************************************************************/
package step.core.collections.mongodb;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.bson.Document;
import org.bson.UuidRepresentation;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import org.mongojack.JacksonMongoCollection;
import org.mongojack.ObjectMapperConfigurer;
import org.mongojack.SerializationOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mongodb.MongoExecutionTimeoutException;
import com.mongodb.MongoNamespace;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.model.CountOptions;
import com.mongodb.client.model.Projections;

import step.core.accessors.AbstractIdentifiableObject;
import step.core.collections.Collection;
import step.core.collections.Filter;
import step.core.collections.SearchOrder;
import step.core.collections.AbstractCollection;

public class MongoDBCollection extends AbstractCollection implements Collection {
	
	private static final Logger logger = LoggerFactory.getLogger(MongoDBCollection.class);

	protected static final String CSV_DELIMITER = ";";
	
	private final MongoClientSession mongoClientSession;
	private final Class entityClass;

	private JacksonMongoCollection collection;
	
	/**
	 * @param collectionName the name of the mongo collection
	 * @param entityClass the
	 */
	public MongoDBCollection(MongoClientSession mongoClientSession, String collectionName, Class entityClass) {
		this.mongoClientSession = mongoClientSession;
		this.entityClass = entityClass;
		collection = JacksonMongoCollection.builder()
				.withObjectMapper(ObjectMapperConfigurer.configureObjectMapper(MongoDBCollectionJacksonMapperProvider.getObjectMapper()))
				.withSerializationOptions(SerializationOptions.builder().withSimpleFilterSerialization(true).build())
				.build(mongoClientSession.getMongoDatabase(), collectionName, entityClass, UuidRepresentation.JAVA_LEGACY);
	}
	
	@Override
	public long count(Filter filter, Integer limit) {
		Bson query = filterToQuery(filter);
		CountOptions countOptions = new CountOptions();
		if(limit != null) {
			countOptions.limit(limit);
		}
		return collection.countDocuments(query, countOptions);
	}

	@Override
	public long estimatedCount() {
		return collection.estimatedDocumentCount();
	}

	/**
	 * @param columnName the name of the column (field)
	 * @return the distinct values of the column 
	 */
	@Override
	public List distinct(String columnName, Filter filter) {
		Bson query = filterToQuery(filter);
		
		if (columnName.equals(AbstractIdentifiableObject.ID)) {
			return collection.distinct("_id", query, ObjectId.class).map(ObjectId::toString).into(new ArrayList());
		} else {
			return collection.distinct(columnName, query, String.class).into(new ArrayList());
		}
	}
	
	private Bson filterToQuery(Filter filter) {
		return new MongoDBFilterFactory().buildFilter(filter);
	}
	
	@Override
	public Stream find(Filter filter, SearchOrder order, Integer skip, Integer limit, int maxTime) {
		return find(filter, order, skip, limit, maxTime, null);
	}

	@Override
	public Stream findReduced(Filter filter, SearchOrder order, Integer skip, Integer limit, int maxTime, List reduceFields ) {
		return find(filter, order, skip, limit, maxTime, reduceFields);
	}

	private Stream find(Filter filter, SearchOrder order, Integer skip, Integer limit, int maxTime, List reduceFields ) {
		Bson query = filterToQuery(filter);
		//long count = collection.estimatedDocumentCount();
		
		//CountOptions option = new CountOptions();
		//option.skip(0).limit(DEFAULT_LIMIT);
		//long countResults = collection.countDocuments(query, option);
		
		FindIterable find = (reduceFields != null && !reduceFields.isEmpty()) ?
				collection.find(query).projection(Projections.include(reduceFields)).maxTime(maxTime, TimeUnit.SECONDS).batchSize(mongoClientSession.getBatchSize()) :
				collection.find(query).maxTime(maxTime, TimeUnit.SECONDS).batchSize(mongoClientSession.getBatchSize());

		if (order != null) {
			String attributeName = fixAttributeName(order.getAttributeName());
			Document sortDoc = new Document(attributeName, order.getOrder());
			find.sort(sortDoc);
		}
		if(skip!=null) {
			find.skip(skip);
		}
		if(limit!=null) {
			find.limit(limit);
		}
		MongoCursor iterator;
		try {
			iterator = find.iterator();
		} catch (MongoExecutionTimeoutException e) {
			logger.error("Query execution exceeded timeout of " + maxTime + " " + TimeUnit.SECONDS);
			throw e;
		}
		
		Iterator enrichedIterator = new Iterator() {
			@Override
			public boolean hasNext() {
				return iterator.hasNext();
			}

			@Override
			public T next() {
				T next = iterator.next();
				fixIdAfterRead(next);
				return next;
			}
		};
		
		return StreamSupport.stream(Spliterators.spliteratorUnknownSize(enrichedIterator, Spliterator.ORDERED), false);
	}

	private String fixAttributeName(String attributeName) {
		if(attributeName.equals(AbstractIdentifiableObject.ID)) {
			attributeName = "_id";
		} else if (attributeName.contains("."+AbstractIdentifiableObject.ID)) {
			attributeName = attributeName.replace("."+AbstractIdentifiableObject.ID, "._id");
		}
		return attributeName;
	}

	private void fixIdAfterRead(T next) {
		if(next instanceof step.core.collections.Document) {
			step.core.collections.Document document = (step.core.collections.Document) next;
			Object id = document.get("_id");
			if(id instanceof ObjectId) {
				id = id.toString();
			}
			document.put(AbstractIdentifiableObject.ID, id);
			document.remove("_id");
		}
	}
	
	private void fixIdBeforeSave(T next) {
		if(next instanceof step.core.collections.Document) {
			step.core.collections.Document document = (step.core.collections.Document) next;
			Object id = document.get(AbstractIdentifiableObject.ID);
			if(id instanceof String) {
				id = new ObjectId((String) id);
			}
			document.put("_id", id);
			document.remove(AbstractIdentifiableObject.ID);
		}
	}

	@Override
	public void remove(Filter filter) {
		collection.deleteMany(filterToQuery(filter));
	}

	@Override
	public T save(T entity) {
		if(getId(entity) == null) {
			setId(entity, new ObjectId());
		}
		fixIdBeforeSave(entity);
		collection.save(entity);
		fixIdAfterRead(entity);
		return entity;
	}

	@Override
	public void save(Iterable entities) {
		entities.forEach(this::save);
	}

	@Override
	public void createOrUpdateIndex(String field) {
		createOrUpdateIndex(field, 1);
	}

	@Override
	public void createOrUpdateIndex(String field, int order) {
		createOrUpdateIndex(collection, field, order);
	}

	@Override
	public void createOrUpdateCompoundIndex(String... fields) {
		Map fieldsMap = new LinkedHashMap<>();
		Arrays.asList(fields).forEach(s -> fieldsMap.put(s,1));
		createOrUpdateCompoundIndex(fieldsMap);
	}

	@Override
	public void createOrUpdateCompoundIndex(Map fields) {
		createOrUpdateCompoundIndex(collection, fields);
	}


	public static void createOrUpdateIndex(com.mongodb.client.MongoCollection collection, String attribute, int order) {
		Document index = getIndex(collection, Set.of(attribute));
		if(index==null) {
			collection.createIndex(new Document(attribute,order));
		}
	}

	public static void createOrUpdateCompoundIndex(com.mongodb.client.MongoCollection collection, Map fields) {
		Document index = getIndex(collection, fields.keySet());
		
		if(index==null) {
			Document newIndex = new Document();
			
			for(String key : fields.keySet())
				newIndex.append(key, fields.get(key));

			collection.createIndex(newIndex);
		}
	}
	
	private static Document getIndex(com.mongodb.client.MongoCollection collection, Set attributes) {

		for(Document index:collection.listIndexes()) {  // inspect all indexes, looking for a match
			Object o = index.get("key");

			if(o instanceof Document) {
				Document d = ((Document)o);
				
				if(attributes.equals(d.keySet())) {
					return d;
				}
			}
		}
		return null;
	}

	@Override
	public void rename(String newName) {
		collection.renameCollection(new MongoNamespace(mongoClientSession.getMongoDatabase().getName(), newName));
	}

	@Override
	public void drop() {
		collection.drop();
	}

	@Override
	public Class getEntityClass() {
		return entityClass;
	}

//	/**
//	 * Export data to CSV
//	 * @param query
//	 * @param columns
//	 * @param writer
//	 */
//	public void export(Filter filter, Map columns, PrintWriter writer) {
//		Bson query = filterToQuery(filter);
//		FindIterable find = collection.find(query);
//		MongoCursor iterator;
//		iterator = find.iterator();
//		if (!iterator.hasNext()) {
//			return;
//		}
//		BasicDBObject basicDBObject = iterator.next();
//		//if column names not provided by the caller, get them from the collection
//		if (columns == null || columns.size() == 0) {
//			columns = getExportFields();
//		}
//		//if the collection has also no specification, dump all keys found in first object
//		if (columns == null || columns.size() == 0 && iterator.hasNext()) {
//			columns = getExportFields();		
//			for (String key : basicDBObject.keySet()) {
//				columns.put(key, new CollectionField(key,key));			
//			}
//		}
//		//write headers
//		columns.values().forEach((v)->{
//			String title = v.getTitle().replaceAll("^ID", "id");
//			writer.print(title);
//			writer.print(CSV_DELIMITER);
//		});
//		writer.println();
//		//Dump first row (required when writting all keys found in 1st object)
//		dumpRow(basicDBObject,columns,writer);
//
//		int count = 1;
//		while(iterator.hasNext()) {
//			count++;
//			basicDBObject = iterator.next();
//			dumpRow(basicDBObject,columns,writer);
//		}
//	}
//	
//	private void dumpRow(BasicDBObject basicDBObject, Map fields, PrintWriter writer) {
//		fields.forEach((key,field)->{
//			Object value = basicDBObject.get(key);
//			if (value != null) {
//				String valueStr = field.getFormat().format(value);
//				if(valueStr.contains(CSV_DELIMITER)||valueStr.contains("\n")||valueStr.contains("\"")) {
//					valueStr = "\"" + valueStr.replaceAll("\"", "\"\"") + "\"";
//				}
//				writer.print(valueStr);
//			}
//			writer.print(CSV_DELIMITER);
//		});
//		writer.println();
//	}
//
//	protected Map getExportFields() {
//		Map result = new HashMap ();
//		return result;
//	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy