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}
Related
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/
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);
}
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 am looking for a solution without spring data. My project requirement is to do without spring data.
To calculate the sum using aggregate function by mongo command, able to get output. But same by using spring data getting exception.
Sample mongo query :
db.getCollection('events_collection').aggregate(
{ "$match" : { "store_no" : 3201 , "event_id" : 882800} },
{ "$group" : { "_id" : "$load_dt", "event_id": { "$first" : "$event_id" }, "start_dt" : { "$first" : "$start_dt" }, "count" : { "$sum" : 1 } } },
{ "$sort" : { "_id" : 1 } },
{ "$project" : { "load_dt" : "$_id", "ksn_cnt" : "$count", "event_id" : 1, "start_dt" : 1, "_id" : 0 } }
)
Same thing done in java as,
String json = "[ { \"$match\": { \"store_no\": 3201, \"event_id\": 882800 } }, { \"$group\": { \"_id\": \"$load_dt\", \"event_id\": { \"$first\": \"$event_id\" }, \"start_dt\": { \"$first\": \"$start_dt\" }, \"count\": { \"$sum\": 1 } } }, { \"$sort\": { \"_id\": 1 } }, { \"$project\": { \"load_dt\": \"$_id\", \"ksn_cnt\": \"$count\", \"event_id\": 1, \"start_dt\": 1, \"_id\": 0 } } ]";
BasicDBList pipeline = (BasicDBList) JSON.parse(json);
System.out.println(pipeline);
AggregationOutput output = col.aggregate(pipeline);
exception is :
com.mongodb.CommandFailureException: { "serverUsed" : "somrandomserver/10.10.10.10:27001" , "errmsg" : "exception: pipeline element 0 is not an object" , "code" : 15942 , "ok" : 0.0}
Could someone please suggest how to use aggregate function with spring?
Try the following (untested) Spring Data MongoDB aggregation equivalent
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
MongoTemplate mongoTemplate = repository.getMongoTemplate();
Aggregation agg = newAggregation(
match(Criteria.where("store_no").is(3201).and("event_id").is(882800)),
group("load_dt")
.first("event_id").as("event_id")
.first("start_dt").as("start_dt")
.count().as("ksn_cnt"),
sort(ASC, previousOperation()),
project("ksn_cnt", "event_id", "start_dt")
.and("load_dt").previousOperation()
.and(previousOperation()).exclude()
);
AggregationResults<OutputType> result = mongoTemplate.aggregate(agg,
"events_collection", OutputType.class);
List<OutputType> mappedResult = result.getMappedResults();
As a first step, filter the input collection by using a match operation which accepts a Criteria query as an argument.
In the second step, group the intermediate filtered documents by the "load_dt" field and calculate the document count and store the result in the new field "ksn_cnt".
Sort the intermediate result by the id-reference of the previous group operation as given by the previousOperation() method.
Finally in the fourth step, select the "ksn_cnt", "event_id", and "start_dt" fields from the previous group operation. Note that "load_dt" again implicitly references an group-id field. Since you do not want an implicit generated id to appear, exclude the id from the previous operation via and(previousOperation()).exclude().
Note that if you provide an input class as the first parameter to the newAggregation method the MongoTemplate will derive the name of the input collection from this class. Otherwise if you don’t not specify an input class you must provide the name of the input collection explicitly. If an input-class and an input-collection is provided the latter takes precedence.