I need to add the fields(get from ui) which need to be fetched using mongodb Aggregation,
From uri i will get param as fields which has comma seperated string of fields
http://foo.com?fields=id,name
A document looks like:
{
"_id" : "3a237c007a87d",
"name" : "Available",
"is_active" : true,
}
The below will work as i want and produce the result
Aggregation aggregation = newAggregation(
project(fields.contains("name") ? "name" : "",
fields.contains("id") ? "id" : ""),
fields.contains("is_active") ? "is_active" : ""),
skip((page-1)*limit),
limit(limit)
);
The above query gets what i want and its shown belo
{
"_id" : "3a237c007a87d",
"name" : "Available"
}
IF i run with below query i m getting atleast one field need to specify in project
And the code:
ProjectionOperation project = project();
for(String field : fields) {
project.andInclude(field);
}
but the field is not adding in projectionOperation
if projectOperation need to like to have
{ "$project" : {"id":1, "name":1 } }
Aggregation aggregation = newAggregation(
project,
skip((page-1)*limit),
limit(limit)
);
The output need to be
{
"_id" : "3a237c007a87d",
"name" : "Available"
}
I like to avoid check whether the list contain the field as they want in project.
Is it possible that you're just confusing some variables here (fields vs fieldList)? Also, you need to use the return value of the andInclude() call Try this instead:
List<String> fieldList = new ArrayList<>();
fieldList.add("id");
fieldList.add("name");
ProjectionOperation project = project();
for(String field : fieldList) { // note that I'm using 'fieldList' here
project = project.andInclude(field);
}
Related
MatchOperation matchDepartmentStage = Aggregation.match(Criteria.where("department").is("DEPT_A"));
ProjectionOperation projectEmployeeStage = Aggregation.project()
.andExpression("concat(emp_name,'-', emp_age)").as("emp_name_age");
Criteria criteriaNameAndAge = new Criteria();
List<Pair<String,String>> nameAndAgeList = new ArrayList<>();
nameAndAgeList.add(Pair.of("abcd", "30"));
nameAndAgeList.forEach(nameAndAge -> {
criteriaNameAndAge.and("emp_name_age").ne(nameAndAge.getFirst()+"-"+nameAndAge.getSecond());
});
MatchOperation matchNameAndAgeStage = Aggregation.match(criteriaNameAndAge);
SortOperation sortStage = Aggregation.sort(Sort.Direction.ASC, "dateCreated");
Aggregation aggregation = Aggregation.newAggregation(matchDepartmentStage,
projectEmployeeStage,
matchNameAndAgeStage,
sortStage);
Employee employee = null;
AggregationResults<Employee> employeeAggregationResults =
mongoTemplate.aggregate(aggregation, "employees", Employee.class);
My attempt is to do as following
Filter employees by department
From the result, for every employee, project a new field that combines 2 fields, emp_name & emp_age to emp_name_age
Create a dynamic match criteria to match this new field
Sort by date
It does not work as expected. Error is invalid reference 'dateCreated'
Sample documents:
{ "_id" : "1", "department" : "DEPAT_A", "name" : "abcd", "age" : "30", "dateCreated" : ISODate("2019-12-31T10:30:00Z"), "_class" : "org.example.Employee" }
{ "_id" : "2", "department" : "DEPAT_A", "name" : "abcd", "age" : "40", "dateCreated" : ISODate("2019-12-31T10:30:00Z"), "_class" : "org.example.Employee" }
EDIT
changed a few lines and it is working. 2 problems identified.
Project has to mention the fields. In my case, name all fields, which I didnt like.
Use nin instead of ne
ProjectionOperation projectEmployeeStage = Aggregation.project(Field.fields("department","name","age","dateCreated"))
.andExpression("concat(emp_name,'-', emp_age)").as("emp_name_age");
List<String> nameAges = nameAndAgeList.stream()
.map(nameAge -> nameAge.getFirst()+"-"+nameAge.getSecond())
.collect(Collectors.toList());
Criteria criteriaNameAndAge = new Criteria("emp_name_age").nin(nameAges);
MatchOperation matchNameAndAgeStage = Aggregation.match(criteriaNameAndAge);
suppose that I have mongoDB collection that has documents like below
{
"_id" : ObjectId("5cb2dd4d378a8e2484e7bb69"),
"name":"ali",
"creator" : {
"name":"user1",
"code":2,
"type" : "desk",
"type_id" : "desk::bb36640a-e384-45ec-aafa-ce71b724c389"
},
"creatorPosition" : {
"name":"user2",
"role":"admin",
"type" : "position",
"type_id" : "position::9f711bb3-3aad-4936-b75a-4ddd024b4bb1"
},
"creatorDetails" : {
"name":"user1",
"type" : "deskUserAssignment",
"type_id" : "deskUserAssignment::dd46fa14-0fd3-43d7-8312-471bc253ee80"
}
}
I want to keep two key values ("type", "type_id") from each fields and remove extra keys (such as "name", "code", "role")
Can I handle this in one mongo query?
In addition, I want to write this code in java
The simplest way to do is using Projection that MongoTemplate offers
For example including fields using org.springframework.data.mongodb.core.query.Query and org.springframework.data.mongodb.core.MongoTemplate for retrieving the results:
Query query = new Query();
query.fields().include("creator.type").include("creator.type_id");
query.fields().include("creatorPosition.type").include("creatorPosition.type_id");
query.fields().include("creatorDetails.type").include("creatorDetails.type_id");
List<Document> documents = mongoTemplate.find(query, Document.class, "your_collection");
For more information see this Article: https://www.mkyong.com/mongodb/spring-data-mongodb-select-fields-to-return/
This is the values of my data stored in mongo db. How am I able to retrieve all the data of "HomeTown" and store it all into a list? My list would contain AA, AA, BB, BB, etc... I want to use that array list to create a for loop of each Hometown.
Sample mongo data
[{ "_id" : { "$oid" : "4ceb753a70fdf877ef5113ca"} , "HomeTown" : "AA" ,
"PhoneNumber" : { "CustName" : "xxx" , "Number" : "3403290"},
"MobileNumber" : { "CustName" : "yyy" , "Number" : "9323304302"}}]
[{ "_id" : { "$oid" : "4ceb753a70fdf877ef5113ca"} , "HomeTown" : "AA" ,
"PhoneNumber" : { "CustName" : "xxx" , "Number" : "3403290"},
"MobileNumber" : { "CustName" : "yyy" , "Number" : "9323304302"}}]
[{ "_id" : { "$oid" : "4ceb753a70fdf877ef5113ca"} , "HomeTown" : "BB" ,
"PhoneNumber" : { "CustName" : "xxx" , "Number" : "3403290"},
"MobileNumber" : { "CustName" : "yyy" , "Number" : "9323304302"}}]
[{ "_id" : { "$oid" : "4ceb753a70fdf877ef5113ca"} , "HomeTown" : "BB" ,
"PhoneNumber" : { "CustName" : "xxx" , "Number" : "3403290"},
"MobileNumber" : { "CustName" : "yyy" , "Number" : "9323304302"}}]
How can I get all of the values of "HomeTown" in Java into an array? I am trying to create a for loop with the HomeTown Names. I am currently using mongodb dependency through Spring boot. I am not sure how would I implement mongodb into my java to use mongo db.
Attempt/Problem
I was able to retrieve mongodb values in a list using the following code. I am trying to convert this list to a arraylist.
public List<AppPortModel> getAppPortList() {
List<ApServerModel> objLst = mongoOperations.findAll(ApServerModel.class);
String[] apServerArray = new String[objLst.size()];
for(int i = 0; i < objLst.size(); i++) {
apServerArray[i] = objLst.get(i);
}
Error on objLst.get(i)
Type mismatch: cannot convert from ApServerModel to String
Attempt #2 Following Sagar Example
#Autowired
MongoOperations mongoOperations;
MongoCollection<ApServerModel> myCollection = **mongoOperations.getCollection("apAllCustomer");**
List<ApServerModel> result = myCollection.find().into(new ArrayList<>());
Error on mongoOperations.getCollection
Type mismatch: cannot convert from DBCollection to MongoCollection<ApServerModel>
Looks like you're using mongo 3.x driver.You'll need to use something like this.
MongoClient mongoClient = new MongoClient();
MongoDatabase db = mongoClient.getDatabase("mkoydb");
MongoCollection<Document> myCollection = db.getCollection("apAllCustomer");
List<Document> result = myCollection.find().into(new ArrayList<>());
Fix for Attempt 1:
public List<AppPortModel> getAppPortList() {
List<ApServerModel> objLst = mongoOperations.findAll(ApServerModel.class);
String[] apServerArray = new String[objLst.size()];
for(int i = 0; i < objLst.size(); i++) {
apServerArray[i] = objLst.get(i).getHomeTown();
}
Fix for Attempt 2:
DBCollection dbCollection = mongoOperations.findAll(AppServreModel.class, "apAllCustomer");
You can call toArray() which, intuitively, returns a List<DBObject>.
List<DBObject> myList = null;
DBCursor myCursor=myCollection.find(new BasicDBObject(),"HomeTown");
myList = myCursor.toArray();
if you are using java driver with version 3.0 and above you can also do
collection.find().projection(Projections.include("HomeTown"));
You can use projection to retrieve only required fields.
db.apAllCustomer.find( ..., { HomeTown: 1 } );
In Java it depends on the API you use. See this for Spring Data:
SpringData MongoDB Using projection
MongoDB-Java:
Restrict fields in Result
Mongo document:
{
"_id" : "1",
"array" : [
{
"item" : "item"
},
{
"item" : "item"
}
]
}
My mongo shell query looks like so:
db.getCollection('collectionName').aggregate(
{$match: { _id: "1"}},
{$project: { count: { $size:"$array" }}}
)
Is there anyway to implement this using the Mongo Template from Spring?
So far I have this:
MatchOperation match = new MatchOperation(Criteria.where("_id").is("1"));
ProjectionOperation project = new ProjectionOperation();
Aggregation aggregate = Aggregation.newAggregation(match, project);
mongoTemplate.aggregate(aggregate, collectionName, Integer.class);
I think I am only missing the project logic but I'm not sure if it is possible to do $size or equivalent here.
It's quite possible, the $size operator is supported (see DATAMONGO-979 and its implementation here). Your implementation could follow this example:
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
Aggregation agg = new Aggregation(
match(where("_id").is("1")), //
project() //
.and("array") //
.size() //
.as("count")
);
AggregationResults<IntegerCount> results = mongoTemplate.aggregate(
agg, collectionName, Integer.class
);
List<IntegerCount> intCount = results.getMappedResults();
Please find below the sample code. You can change it accordingly for your requirement with collection name, collection name class and array field name.
MatchOperation match = new MatchOperation(Criteria.where("_id").is("1"));
Aggregation aggregate = Aggregation.newAggregation(match, Aggregation.project().and("array").project("size").as("count"));
AggregationResults<CollectionNameClass> aggregateResult = mongoOperations.aggregate(aggregate, "collectionName", <CollectionNameClass>.class);
if (aggregateResult!=null) {
//You can find the "count" as an attrribute inside "result" key
System.out.println("Output ====>" + aggregateResult.getRawResults().get("result"));
System.out.println("Output ====>" + aggregateResult.getRawResults().toMap());
}
Sample output:-
Output ====>[ { "_id" : "3ea8e671-1e64-4cde-bd78-5980049a772b" , "count" : 47}]
Output ====>{serverUsed=127.0.0.1:27017, waitedMS=0, result=[ { "_id" : "3ea8e671-1e64-4cde-bd78-5980049a772b" , "count" : 47}], ok=1.0}
You can write query as
Aggregation aggregate = Aggregation.newAggregation(Aggregation.match(Criteria.where("_id").is(1)),
Aggregation.project().and("array").size().as("count")); mongoTemplate.aggregate(aggregate, collectionName, Integer.class);
It will execute the following query { "aggregate" : "collectionName" , "pipeline" : [ { "$match" : { "_id" : 1}} , { "$project" : { "count" : { "$size" : [ "$array"]}}}]}
I'm using Spring Data Mongodb and documents like this :
{
"_id" : ObjectId("565c5ed433a140520cdedd7f"),
"attributes" : {
"565c5ed433a140520cdedd73" : "333563851"
},
"listId" : ObjectId("565c57aaf316d71fd4e4d0a0"),
"international" : false,
"countryCode" : 33,
"deleted" : false
}
I would like to query my collection to search a value in the attributes object (in the values and not the keys).
For example like this :
#Query("{'listId': ?0, $or: [{'attributes.*':{$regex: ?1}}], 'deleted':false}")
Page<PTContact> findByListIdAndByAttributesContaining(ObjectId listId, String val, Pageable pageable);
But I'm not sure to be able to do that. Any idea to help me ?
Best regards
edit: my solution
Well, what I've done is simple. I know every id (key in attributes fields) for that listId so I create a custom mongo opération
Criteria[] fieldsCriteria = new Criteria[fields.size()];
for (int i = 0; i < fields.size(); i++) {
fieldsCriteria[i] = Criteria.where("attributes." + fields.get(i).getId()).regex(val, "i");
}
Query query = Query.query(Criteria.where("listId").is(listId).orOperator(fieldsCriteria));
return new PageImpl<>(operations.find(query.with(pageable), PTContact.class), pageable, operations.count(query, PTContact.class));
With some pagination..
And it works
I think that you have to change the structure of attributes :
attributes : [{
key : "565c5ed433a140520cdedd73",
value: "333563851"
}, {
key : "...",
value: "..."
}
]
Then change you request like this :
{'attributes.value': ?1}