The database MongoDB I have stored documents in the format:
{
"achievement": [
{
"userFromId":"max",
"userToId":"peter",
"date":"2016-01-25",
"pointCount":1,
"description":"good work",
"type":"THANKS"
}
]
}
How to get the number of records in the database (if any) for the a certain date, in which people are thanking the other people.
I created a query to retrieve data:
DBObject clause1 = new BasicDBObject("userFromId", userFromId);
DBObject clause2 = new BasicDBObject("userToId", userToId);
DBObject clause3 = new BasicDBObject("sendDate", localDate);
DBObject clause4 = new BasicDBObject("type", Thanks);
BasicDBList or = new BasicDBList();
or.add(clause1);
or.add(clause2);
or.add(clause3);
or.add(clause4);
DBObject query = new BasicDBObject("$or", or);
But I do not know how to get the number of records and how can rewrite the query using aggregation?
For example:
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group("userFromId")
.first("userFromId").as("userFromId")
.sum("pointCount").as("pointCount"));
I do not know how to add a few more parameters.
What the return request if the data to the database does not exist?
Thanks for any help
You can use something like this. This will count all the number of documents matching the below criteria.
Regular Query
db.collection.count({ $or: [ { "userFromId": userFromId }, { "userToId": userToId } ] });
Using Aggregation
db.collection.aggregate( [
{ $match: { $or: [ { "userFromId": userFromId }, { "userToId": userToId } ] } },
{ $group: { _id: null, count: { $sum: 1 } } }
] );
Related
I have a requirement of fetching data from mongodb and being done using Java Spring Reactive Mongodb library. I am using the following code for this:
Criteria criteria = Criteria.where(QUERYFIELD1).in(listOfIds)
.andOperator(Criteria.where(QUERYFIELD2).gte(ChildDate));
Query query = Query.query(criteria).noCursorTimeout();
reactiveMongoTemplate.find(query, Document.class, COLLECTIONNAME);
Where QUERYFIELD1 is "ChildId" and QUERYFIELD2 is a "ChildDate". Following is the structure of my document:
{
"_id": {
"$oid": "6296968fa63757a93e1cd123"
},
"Level1": {
"Level2": [
{
"ChildId": "1234",
"ChildDate": {
"$date": "2021-04-01T04:00:00.000Z"
}
},
{
"ChildId": "5678",
"ChildDate": {
"$date": "2017-05-16T04:00:00.000Z"
}
},
{
"ChildId": "3456",
"ChildDate": {
"$date": "2008-09-16T04:00:00.000Z"
}
},
{
"ChildDate": {
"$date": "2022-06-01T04:00:00.000Z"
},
"ChildId": "7891"
}
]
}
}
I am trying to find a document which should match the criteria within the Objects under Level2. For e.g. if My criteria has ChildId as "3456" and ChildDate as "2022-06-01T04:00:00.000Z" then I should get empty results as ChildId is matching with Object3 and ChildDate is matching with Object4. But when I use below query, I get 1 record as the match:
{ "Level1.Level2.ChildId" : "3456", "Level1.Level2.ChildDate" : { $gt: new Date("2022-01-01T05:00:00.000+00:00")}}
I am trying to achieve this using Spring Reactive MongoDB. Please help.
You can use $elemMatch for finding the documents that their array includes an item which matches the conditions:
db.collection.find({
"Level1.Level2": {
$elemMatch: {
ChildId: "3456",
ChildDate: {$gt: new Date("2008-09-16T05:00:00.000Z")}
}
}
})
See how it works on the playground example
How can I translate the following MongoDB query into Java-based query using the Java MongoDB Spring Data driver?
db.User.aggregate([
{ $match : { $expr: { $in: [ "ADMIN", "$roles" ] } } },
{ $sort : { "createdAt": 1 } },
{ $limit : 1 }
])
My attempted solution in Kotlin, which I think is wrong (I don't know how to specify that "roles" is a field in the User document).
fun queryFirstAdmin(): User? {
val matchRolesOpt = Aggregation.match(Criteria.where("ADMIN").`in`("roles"))
val sortOpt = Aggregation.sort(Sort.Direction.ASC, "createdAt")
val limitOpt = Aggregation.limit(1)
var ops: MutableList<AggregationOperation> = mutableListOf()
ops.add(matchRolesOpt)
ops.add(sortOpt)
ops.add(limitOpt)
var aggregation = Aggregation.newAggregation(*ops.toTypedArray())
val aggregationResult = mongoTemplate.aggregate(aggregation, User::class.java, User::class.java)
return aggregationResult.uniqueMappedResult
}
This solution works but unfortunately it returns a slice of the User document (since it has been unwound):
fun queryFirstAdmin(): User? {
val unwindOpt = Aggregation.unwind("roles")
val matchRolesOpt = Aggregation.match(Criteria.where("roles").`is`("ADMIN"))
val sortOpt = Aggregation.sort(Sort.Direction.ASC, "createdAt")
val limitOpt = Aggregation.limit(1)
var ops: MutableList<AggregationOperation> = mutableListOf(unwindOpt, matchRolesOpt, sortOpt, limitOpt)
var aggregation = Aggregation.newAggregation(*ops.toTypedArray())
val aggregationResult = mongoTemplate.aggregate(aggregation, User::class.java, User::class.java)
return aggregationResult.uniqueMappedResult
}
i.e. if the Document returned has the array ["ADMIN"] but the original document has ["ADMIN","USER] in the "roles" field
How can I fix this?
The MongoDB Spring Data Java code for the aggregation:
MongoOperations mongoOps = new MongoTemplate(MongoClients.create(), "test");
Aggregation agg = newAggregation(
match(where("roles").is("ADMIN")),
sort(ASC, "createdAt"),
limit(1L)
);
AggregationResults<Document> results = mongoOps.aggregate(agg, "User", Document.class);
results.forEach(System.out::println);
NOTE:
The match stage in the aggregation
{ $match : { $expr: { $in: [ "ADMIN", "$roles" ] } } }
can be replaced with this and the result is the same:
{ $match : { roles: "ADMIN" } }
The same query using MongoDB Java Driver:
Bson match = match(expr( Document.parse(" { $in: [ 'ADMIN', '$roles' ] } ")));
// -or-
// Bson match = match(eq("roles", "ADMIN"));
List<Bson> pipeline =
Arrays.asList(match, sort(ascending("createdAt")), limit(1));
List<Document> results = new ArrayList<>();
collection.aggregate(pipeline).into(results);
results.forEach(System.out::println);
I tried to write a group aggregation query using the year value from a date object as a key, but for some reason I'm getting this exception.
org.springframework.data.mapping.PropertyReferenceException: No property year(invoiceDate)
Here is the mongo query which I'm trying to replicate:
db.collection.aggregate([
{
$match:
{
"status": "Active"
}
},
{
$group:
{
"_id":{$year:"$invoiceDate"}
}
},
{
$sort:
{
"_id" : -1
}
}
])
And this is my Java implementation:
Aggregation aggregation = Aggregation.newAggregation(
match(new Criteria().andOperator(criteria())),
Aggregation.group("year(invoiceDate)")
).withOptions(newAggregationOptions().allowDiskUse(true).build());
I also didn't find a way how I can apply the sorting on the results from the grouping.
You're basically looking for extractYear() which maps to the $year operator with MongoDB:
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(new Criteria().andOperator(criteria())),
Aggregation.project().and("invoiceDate").extractYear().as("_id"),
Aggregation.group("_id"),
Aggregation.sort(Sort.Direction.DESC, "_id)
)
This generally needs to go into a $project in order to make the helpers happy.
If you really want the expression within the $group then you can add a custom operation expression:
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(new Criteria().andOperator(criteria())),
new AggregationOperation() {
#Override
public Document toDocument(AggregationOperationContext aggregationOperationContext) {
return new Document("$group",
new Document("_id", new Document("$year","$invoiceDate") )
);
}
},
Aggregation.sort(Sort.Direction.DESC, "_id)
)
I have a document like this:
solution:{
"name":"solution name",
"desc":"description",
"case":[
{
"A":13,
"B":"aaaa"
},
{
"A":14,
"B":"aaaa"
},
{
"A":13,
"B":"bbbb"
}
]
}
The case is an array field in solution table, and it contains two fields, field A and filed B.
Now I can query the solution record, and the return result will include all case elements. Now I wish the query result only includes case elements which filed B is "aaaa", how to write the query in java or MongoDB?
My expected query result should be like this:
solution:{
"name":"solution name",
"desc":"description",
"case":[
{
"A":13,
"B":"aaaa"
},
{
"A":14,
"B":"aaaa"
},
]
}
You can use aggregate pipeline $redact to keep only matched objects,
Mongo shell command,
db.solution.aggregate([ {$redact: {
"$cond": [{
"$eq": [{
"$ifNull": ["$B", "aaaa"]
},
"aaaa"
]
}, "$$DESCEND", "$$PRUNE"]
}}]).pretty()
Mongo Java code,
MongoClient client = new MongoClient("localhost");
MongoDatabase db = client.getDatabase("Test");
MongoCollection<Document> collection = db.getCollection("solution");
List<Document> results = collection.aggregate(Arrays.asList(
new Document("$redact", new Document("$cond",
Arrays.asList(new Document("$eq",
Arrays.asList(new Document("$ifNull", Arrays.asList("$B", "aaaa")), "aaaa")),
"$$DESCEND", "$$PRUNE")))
)).into(new ArrayList<Document>());
for(Document docs: results){
System.out.println(docs.toJson());
}
You may use $filter aggregation to filter an array based on a condition during projection
db.solution.aggregate([
{$match: {'case.B': "aaaa"}},
{$project: {
case: {$filter: {
input: '$case',
as: 'case',
cond: {$eq: ['$$case.B', "aaaa"]}
}},
name:1,
desc:1
}}
])
I'm using mongodb to store data for my java program and I have a collection with an array field that has a lot of things in it but i want only to get the length, without all the other data.
Now i'm using this to get it:
((UUID[])document.get("customers")).length
How can I make this not to download all the array?
A possible answer is to create an int that counts the pushes and the pulls of the array but it's not the cleanest method.
You are looking for aggregation framework where you can use the $size operator in your pipeline, this counts and returns the total the number of items in an array:
db.collection.aggregate([
{
"$project": {
"_id": 0, "customer_count": { "$size": "$customers" }
}
}
]);
where the Java equivalent:
DBObject projectFields = new BasicDBObject("_id", 0);
projectFields.put("customer_count", new BasicDBObject( "$size", "$customers" ));
DBObject project = new BasicDBObject("$project", projectFields);
AggregationOutput output = db.getCollection("collectionName").aggregate(project);
System.out.println("\n" + output);
You can use MongoDB's Aggregation Framework to get the size of the array. For example, given the following document structure:
> db.macross.findOne()
{
"_id" : "SDF1",
"crew" : [
"Rick",
"Minmay",
"Roy",
"Max",
"Misa",
"Milia"
]
}
get the size of the array
> db.macross.aggregate(
{ $match: { _id: "SDF1" } },
{ $unwind: "$crew" },
{ $group: { _id: "", count: { $sum: 1 } } },
{ $project: { _id: 0, count: 1 } }
)
{ "count" : 6 }
More detailed and interesting examples are available in the docs.