Generic Search in MongoDB With Java
There is no need to put a Service Class for every collection — one method could do that!
Join the DZone community and get the full member experience.
Join For FreeIn this post, we will create a generic method to search data in MongoDB database via MongoDB driver API. There is no need to put specific Service Class for every collection; one method could do that
-
QueryParam
: Class to specify field name, value, and operator (equal, greather than, etc.)
package com.ahajri.mongo.queries;
public class QueryParam{
//collection field name
private String fieldName;
//Operator name
private String operator;
//value
private Object value;
public QueryParam(String fieldName, String operator, Object value) {
super();
this.fieldName = fieldName;
this.operator = operator;
this.value = value;
}
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
Operators are grouped in a Java enum, as shown below:
package com.ahajri.mongo.enums;
/**
*
* @author ahajri
*
*
* <p>
* $eq Matches values that are equal to a specified value.
* </p>
* <p>
* $gt Matches values that are greater than a specified value.
* </p>
* <p>
* $gte Matches values that are greater than or equal to a specified
* value.
* </p>
* <p>
* $in Matches any of the values specified in an array.
* </p>
* <p>
* $lt Matches values that are less than a specified value.
* </p>
* <p>
* $lte Matches values that are less than or equal to a specified value.
* </p>
* <p>
* $ne Matches all values that are not equal to a specified value.
* </p>
* <p>
* $nin Matches none of the values specified in an array.
* </p>
*
*/
public enum OperatorEnum {
EQ, GT, GTE, IN, LT, LTE, NE, NIN;
}
Now, let's explore our Mongo service accessor class; our database is supposed to be hosted on MLab, and application.yml contains the required information, as shown below:
mlab:
db.host: ${MLAB_DB_HOST}
db.name: ${MLAB_DB_NAME}
db.user: ${MLAB_DB_USER}
db.port: ${MLAB_DB_PORT}
db.password: ${MLAB_DB_PASSWORD}
api.key: ${MLAB_API_KEY}
spring:
data.mongodb.uri: mongodb://${MLAB_DB_USER}:${MLAB_DB_PASSWORD}@${MLAB_DB_HOST}:${MLAB_DB_PORT}/${MLAB_DB_NAME}
servlet.multipart.enabled: true
servlet.multipart.file-size-threshold: 2KB
servlet.multipart.max-file-size: 200MB
servlet.multipart.max-request-size: 215MB
Our search method open and close the connection in an atomic way where we only give the collection name and array of the QueryParam
class. It looks like this:
package com.ahajri.hc.mongo.cloud;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.PostConstruct;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.ahajri.mongo.enums.ErrorMessageEnum;
import com.ahajri.mongo.exception.MyException;
import com.ahajri.mongo.enums.QueryParam;
import com.ahajri.mongo.utils.JsonUtils;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.FindOneAndReplaceOptions;
/**
* @author ahajri
*/
@Service
public class MLabMongoService {
private String cloudMongUrl;
private MongoDatabase db;
private MongoClient client;
@Value("${mlab.api.key}")
protected String apiKey;
@Value("${mlab.db.user}")
protected String dbUser;
@Value("${mlab.db.password}")
protected String dbPwd;
@Value("${mlab.db.name}")
protected String dbName;
@Value("${mlab.db.port}")
protected String dbPort;
@Value("${mlab.db.host}")
protected String dbHost;
public MLabMongoService() {
}
@PostConstruct
public void init() {
//create MLab url connection
cloudMongUrl = "mongodb://" + dbUser + ":" + dbPwd + "@" + dbHost + ":" + dbPort + "/" + dbName;
}
/**
* Open Mongo Connection
*/
private void begin() {
MongoClientURI uri = new MongoClientURI(cloudMongUrl);
client = new MongoClient(uri);
db = client.getDatabase(uri.getDatabase());
}
/**
*
* Search document
*
* @param collectionName:
* Collection name
* @param qp
* : Query Parameters
* @return list of found documents
* @throws BusinessException
*/
public List<Document> search(String collectionName, QueryParam... qp) throws BusinessException {
try {
begin();
MongoCollection<Document> collection = db.getCollection(collectionName);
List<Document> result = new ArrayList<>();
Document query = new Document();
Arrays.asList(qp).stream().forEach(p -> {
String operator = p.getOperator();
String fieldName = p.getFieldName();
Object value = p.getValue();
switch (operator) {
case "EQ":
query.append(fieldName, new Document().append("$eq", value));
break;
case "NE":
query.append(fieldName, new Document().append("$ne", value));
break;
case "GT":
query.append(fieldName, new Document().append("$gt", value));
break;
case "GTE":
query.append(fieldName, new Document().append("$gte", value));
break;
case "LT":
query.append(fieldName, new Document().append("$lt", value));
break;
case "LTE":
query.append(fieldName, new Document().append("$lte", value));
break;
case "IN":
query.append(fieldName, new Document().append("$in", value));
break;
case "NIN":
query.append(fieldName, new Document().append("$nin", value));
break;
default:
break;
}
});
FindIterable<Document> iterable = collection.find(query);
if (iterable.first() == null) {
throw new Exception(ErrorMessageEnum.FIND_DOCUMENT_KO.getMessage());
}
for (Document document : iterable) {
result.add(document);
}
close();
return result;
} catch (Exception e) {
throw new MyException(e, ErrorMessageEnum.FIND_DOCUMENT_KO.getMessage());
}
}
private void close() {
if (client != null) {
client.close();
}
}
}
To test the method,QueryParams
must be provided in a programmatically. For example:
QueryParam[] qps = new QueryParam[1];
qps[0] = new QueryParam("email", OperatorEnum.EQ.name(), "myemail@mail.com");
Hope this was helpful!
Opinions expressed by DZone contributors are their own.
Comments