Struggling to Mock what to return when using AggregateIterable in Java/Mongo.
I have tried using MongoCursor but struggling to understand this. I want to cover all paths with Unit Tests for this. What are the best possible ways using mockito and testNG.
public List<User> getAggregatedList(User user){
AggregateIterable<User> agg = collection.aggregate(pipeline, User.class)
List<User> users = new ArrayList<>();
if (agg != null){
Iterator<User> it = agg.iterator();
while(it.hasNext()){
users.add(it.next());
}
}
return users;
}
I should be able to write a test such as:
...
expected = Arrays.asList(user1,user2...)
verify(result,expect)
You can test it by mocking the hasNext() and next() methods of the iterator like so:
#Test
public void testGetAggregatedList() {
MongoCollection collection = PowerMockito.mock(MongoCollection.class);
AggregateIterable<User> iterable = PowerMockito.mock(AggregateIterable.class);
MongoClient client = PowerMockito.mock(MongoClient.class);
MongoDatabase db = PowerMockito.mock(MongoDatabase.class);
MongoCursor iterator = PowerMockito.mock(MongoCursor.class);
User user1 = PowerMockito.mock(User.class);
User user2 = PowerMockito.mock(User.class);
MongoDaoDelegator.setClient(client);
PowerMockito.when(client.getDatabase(anyString())).thenReturn(db);
PowerMockito.when(db.getCollection(anyString())).thenReturn(collection);
PowerMockito.when(collection.aggregate(anyList(),any())).thenReturn(iterable);
PowerMockito.when(iterable.iterator()).thenReturn(iterator);
PowerMockito.when(iterator.hasNext()).thenReturn(true,true,false);
PowerMockito.when(iterator.next()).thenReturn(user1,user2);
getAggregatedList(new User());
}
This will give you the result you are expecting.
Related
this is my method, when the server runs this mothod, it will take 18s.
but when i run the same query, use NoSQLBoost, it just takes 5s.
public Integer queryUserEduTotal(UserAdminV1StepListRequest body) throws ParseException {
Criteria criteria = Criteria.where(Column.SYSTEM_STATUS).is(true);
criteria.and(UserColumn.APP_ID).is(body.getAppId());
criteria.and(UserColumn.STATUS).is(2);
Criteria criteria1 = Criteria.where(UserColumn.USER_TYPE).exists(false);
Criteria criteria2 = Criteria.where(UserColumn.USER_TYPE).is(UserTypeEnum.USER_REGISTER.getKey());
Criteria criteria3 = new Criteria();
criteria3.orOperator(criteria1, criteria2);
criteria.andOperator(criteria3, Criteria.where(UserColumn.SYSTEM_CREATE_TIME).gte(sdf.parse(body.getStart())), Criteria.where(UserColumn.SYSTEM_CREATE_TIME).lte(sdf.parse(body.getEnd())));
LookupOperation lookup = lookup("user_edu_item_info", "_id", UserEduItemColumn.USER_ID, "edu_info");
Criteria criteria4 = Criteria.where(Column.SYSTEM_STATUS).is(true);
criteria4.and(UserColumn.APP_ID).is(body.getAppId());
criteria4.and("edu_info._id").exists(true);
Aggregation typedAggregation1 = Aggregation.newAggregation(
match(criteria),
lookup,
match(criteria4),
Aggregation.count().as(Constant.COUNT)
);
AggregationResults<BasicDBObject> aggregationResults = mongoTemplate.aggregate(typedAggregation1, User.class, BasicDBObject.class);
int count = 0;
if (aggregationResults.getMappedResults().size() == 0) {
count = 0;
} else {
String document = JSON.toJSONString(aggregationResults.getMappedResults().get(0));
JSONObject result = JSON.parseObject(document);
count = result.getInteger(Constant.COUNT);
}
return count;
}
and, i run the same code on the local by using the java program.it takes 5s too.
i dont know why the server takes so much time.
MongoDB configuration:
MongoClientOptions.Builder builder = new xxx()
builder.connectionsPerHost(100);
builder.minConnectionsPerHost(10);
builder.connectTimeout(30000);
builder.threadsAllowedToBlockForConnectionMultiplier(10);
builder.serverSelectionTimeout(30000);
builder.socketTimeout(0);
builder.maxWaitTime(120000);
builder.heartbeatConnectTimeout(30000);
builder.heartbeatSocketTimeout(30000);
builder.heartbeatFrequency(10000);
builder.minHeartbeatFrequency(500);
builder.localThreshold(15);
this is my local java code
public class Test {
public static void main(String[] args) {
// Link code omitted
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory);
search(mongoTemplate);
try {
mongoDbFactory.destroy();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void search(MongoTemplate mongoTemplate){
Criteria criteria = Criteria.where("systemStatus").is(true);
//criteria code omitted, sane whit the server code
Aggregation typedAggregation1 = Aggregation.newAggregation(
match(criteria),
lookup,
match(criteria4),
Aggregation.count().as("count")
);
AggregationResults<BasicDBObject> aggregationResults = mongoTemplate.aggregate(typedAggregation1, User.class, BasicDBObject.class);
}
}
My controller:
#RequestMapping(value = "/{id}/giveFacilitiesAccess", method = RequestMethod.POST)
public Object giveFacilityAccessToAnotherUser(#PathVariable("id") String userId)
{
return userService.giveFacilitiesAccessToAnotherUser(userId);
}
My service:
#Override
public Object giveFacilitiesAccessToAnotherUser(String id)
{
String userId = getLoggedInUserId();
User u = userDao.findById(userId);
List<String> facilitiesAccess = u.getFacilitiesAccess();
return userDao.giveFacilitiesAccessToAnotherUser(id,facilitiesAccess);
}
My dao:
#Override
public Object giveFacilitiesAccessToAnotherUser(String userId, List<String>facilitiesAccess)
{
Query query = new Query();
query.addCriteria(Criteria.where("id").is(userId));
Update update = new Update().addToSet("facilitiesAccess.",facilitiesAccess);
mongoTemplate.updateFirst(query, update, User.class);
return null;
}
After updating:
"facilitiesAccess":["5f0996f792691d1b68671da3",["5f0998ba92691d1b68671da4"]]
It's updating like array inside another array, but i need in this format:
"facilitiesAccess":["5f0996f792691d1b68671da3","5f0998ba92691d1b68671da4"]
The update works with the syntax using the addToSet(String key) which returns a Update.AddToSetBuilder - then apply the each(Object... values) method on the builder, to return the Update object.
Update update = new Update().addToSet("facilitiesAccess").each(facilitiesAccess);
You need to use Each like below
#Override
public Object giveFacilitiesAccessToAnotherUser(String userId, List<String>facilitiesAccess)
{
Query query = new Query();
query.addCriteria(Criteria.where("id").is(userId));
Update update = new Update().addToSet("facilitiesAccess.",new Each(facilitiesAccess));
mongoTemplate.updateFirst(query, update, User.class);
return null;
}
I'm using this method to send multiple list to client.
public ResponseEntity<?> getFiveLastRequestOfEachVehicleType() {
ResponseContent content = getResponseContent();
Map<String, List<Request>> map = new HashMap<>();
GroupBy groupBy = new GroupBy();
groupBy.initialDocument("vehicleTypeEnum");
query = new Query();
queryFieldsFilterLastFiveRequest(query, VehicleTypeEnum.NEISAN);
map.put("NEISAN", mongoOperations.find(query, Request.class));
query = new Query();
queryFieldsFilterLastFiveRequest(query, VehicleTypeEnum.BADSAN);
map.put("BADSAN", mongoOperations.find(query, Request.class));
query = new Query();
queryFieldsFilterLastFiveRequest(query, VehicleTypeEnum.BUJE);
map.put("BUJE", mongoOperations.find(query, Request.class));
content.setData(map);
return getReturn(content);
}
And this method apply criteria to each query
private void queryFieldsFilterLastFiveRequest(Query query, VehicleTypeEnum vehicleTypeEnum) {
query.addCriteria(Criteria.where("vehicleTypeEnum").is(vehicleTypeEnum));
query.addCriteria(Criteria.where("unlock").is(true));
query.fields()
.include("id")
.include("goodsTypeTitle")
.include("originCityTitle")
.include("price");
query.limit(5);
}
I wonder if there is any way to retrieve all list in one request to database using MongoOperations.
I would go to grouping manually later on, first query for all vehicleType :
private void queryFieldsFilterLastFiveRequest(Query query, VehicleTypeEnum... vehicleTypeEnum..) {
query.addCriteria(Criteria.where("vehicleTypeEnum").in(vehicleTypeEnum));
query.addCriteria(Criteria.where("unlock").is(true));
query.fields()
.include("id")
.include("goodsTypeTitle")
.include("originCityTitle")
.include("price");
query.limit(5);
}
Here the is has been changed by in, and accept a list of VehicleTypeEnum
Then to use it :
public ResponseEntity<?> getFiveLastRequestOfEachVehicleType() {
ResponseContent content = getResponseContent();
Map<String, List<Request>> map;
query = new Query();
queryFieldsFilterLastFiveRequest(query, VehicleTypeEnum.NEISAN, VehicleTypeEnum.BADSAN, VehicleTypeEnum.BUJE);
map = mongoOperations.find(query, Request.class).stream()
.collect(Collectors.toMap(r -> r.getVehicleTypeEnum().name(),r -> r));
content.setData(map);
return getReturn(content);
}
Here i use the stream api to group result by VehiculeTypeEnum name
I have a document having schema like bellow
{
"Personal":[
{
"name":"Test_Name",
"isActive":true
}
]
}
am trying to update this as below using java driver.
collections.updateMany(new Document("Personal.name", "Test_Name"), new Document("$set", new Document("Personal.$.isActive", false)))
But unfortunately this trows an error
"The positional operator did not find the match needed from the query. Unexpanded update: Personal.$.isActive"
But if i modify the above update filter something like
collections.updateMany(new Document("Personal.name", "Test_Name"), new Document("$set", new Document("Personal.0.isActive", false)))
it works.
Can any one help me in understanding whats wrong in using "$" in my 1st update statement?
Here is some more code spinets
Creating the collection object:
collections = mongoConnection.establishConnection()
MongoConnection object:
public MongoConnection() {
StringBuilder connectionString = new StringBuilder();
connectionString.append("mongodb://url_with_port_and_server")
client = new MongoClient(new MongoClientURI(connectionString.toString()));
db = client.getDatabase("test");
collections = db.getCollection("test");
}
public MongoCollection<Document> establishConnection() {
return collections;
}
public void closeConnection() {
client.close();
}
I am trying to do a multiple insert using the camel mongo db component.
My Pojo representation is :
Person {
String firstName;
String lastName;
}
I have a processor which constructs a valid List of Person pojo and is a valid json structure.
When this list of Person is sent to the mongodb producer , on invocation of createDoInsert the type conversion to BasicDBObject fails. This piece of code below looks to be the problem. Should it have more fall backs / checks in place to attempt the list conversion down further below as it fails on the very first cast itself. Debugging the MongoDbProducer the exchange object being received is a DBList which extends DBObject. This causes the singleInsert flag to remain set at true which fails the insertion below as we get a DBList instead of a BasicDBObject :
if(singleInsert) {
BasicDBObject insertObjects = (BasicDBObject)insert;
dbCol.insertOne(insertObjects);
exchange1.getIn().setHeader("CamelMongoOid", insertObjects.get("_id"));
}
The Camel MongoDbProducer code fragment
private Function<Exchange, Object> createDoInsert() {
return (exchange1) -> {
MongoCollection dbCol = this.calculateCollection(exchange1);
boolean singleInsert = true;
Object insert = exchange1.getIn().getBody(DBObject.class);
if(insert == null) {
insert = exchange1.getIn().getBody(List.class);
if(insert == null) {
throw new CamelMongoDbException("MongoDB operation = insert, Body is not conversible to type DBObject nor List<DBObject>");
}
singleInsert = false;
insert = this.attemptConvertToList((List)insert, exchange1);
}
if(singleInsert) {
BasicDBObject insertObjects = (BasicDBObject)insert;
dbCol.insertOne(insertObjects);
exchange1.getIn().setHeader("CamelMongoOid", insertObjects.get("_id"));
} else {
List insertObjects1 = (List)insert;
dbCol.insertMany(insertObjects1);
ArrayList objectIdentification = new ArrayList(insertObjects1.size());
objectIdentification.addAll((Collection)insertObjects1.stream().map((insertObject) -> {
return insertObject.get("_id");
}).collect(Collectors.toList()));
exchange1.getIn().setHeader("CamelMongoOid", objectIdentification);
}
return insert;
};
}
My route is as below :
<route id="uploadFile">
<from uri="jetty://http://0.0.0.0:9886/test"/>
<process ref="fileProcessor"/>
<unmarshal>
<csv>
<header>fname</header>
<header>lname</header>
</csv>
</unmarshal>
<process ref="mongodbProcessor" />
<to uri="mongodb:mongoBean?database=axs175&collection=insurance&operation=insert" />
and the MongoDBProcessor constructing the List of Person Pojo
#Component
public class MongodbProcessor implements Processor {
#Override
public void process(Exchange exchange) throws Exception {
ArrayList<List<String>> personlist = (ArrayList) exchange.getIn().getBody();
ArrayList<Person> persons = new ArrayList<>();
for(List<String> records : personlist){
Person person = new Person();
person.setFname(records.get(0));
person.setLname(records.get(1));
persons.add(person);
}
exchange.getIn().setBody(persons);
}
}
Also requested information here - http://camel.465427.n5.nabble.com/Problems-with-MongoDbProducer-multiple-inserts-tc5792644.html
This issue is now fixed via - https://issues.apache.org/jira/browse/CAMEL-10728