Spring data mongo db count nested objects with a specific condition - spring

I have a document like that:
'subject' : {
'name' :"...."
'facebookPosts':[
{
date:"14/02/2017 20:20:03" , // it is a string
text:"facebook post text here",
other stuff here
}
]
}
and I want to count the facebookPosts within a specific objects that their date field contains e.g "23/07/2016".
Now, I do that by extracting all the documents and count in the client side (spring ) , But I think that's not efficient.

You need to aggregate your results.
final Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("facebookPosts.date").regex(REGEX)),
Aggregation.unwind("facebookPosts"),
Aggregation.group().count().as("count"));
Regex might not be the best solution, just an example.
unwind will split array into separate elements you can then count.
Create a class that will hold the count, something like:
public class PostCount {
private Long count;
// getters, setters
}
And then execute it like this:
AggregationResults<PostCount> postCount = mongoTemplate.aggregate(aggregation, Subject.class, PostCount.class);
long count = postCount.getMappedResults().get(0).getCount();

Related

Write All Data to CSV With For-Loop In Spring-Boot

I have a query in my Spring-Boot project. With this query, I can pull data with limit-offset 100 by 100. After pull it, I want to write all filtered data to CSV. In here actually, I wonder how I can go on.
Repository:
public List<Response> filterData(FilterOptions, limit, offset)
{
sql = "select
name, date, sum ...
from
metadata join report
where filters
limit 100 , offset ..."
//native query mapping to Response entity list.
return responseList.
}
Service:
public void exportData(FilterOptions, totalRows)
{
for (int i = 0; i < totalRows; i=i+100)
{
data = repository.filterData(filterOptions, limit=100, offset=i);
writeToCsv(data);
}
}
In here, in service part; How can I handle writeToCsv part? To use Spring-Batch will be useful for my case? Or to use open-csv? Can I implement parallelism in here?

Lite DB not finding inner object query

I have two objects.
[DataContract]
public class Record
{
[DataMember]
public string Id { get; set; }
}
And this class:
public class BatteryStatus : Record
{
[DataMember]
public DateTime RetrieveTime { get; set; }
}
I'm using Lite DB as a local NoSQL option to query and save the data. I'm needing to find and delete the values based after some time. Here's my code doing so:
var col = db.GetCollection<BatteryStatus>(CollectionName);
var test = col.FindAll()
.Where(x => x.Id == status.Id).ToList();
var result = col.Find(Query.EQ("Id", status.Id.ToString())).ToList();
Test returns with the with the object, but the result value doesn't. Lite DB only uses the Query or the BSONId as a way to delete an object. I don't have a BSON id attached to it (it's a referenced definition so I can't change it).
How can I use the "Query" function in order to get a nested value so I can delete it?
Classes has properties, BSON documents has fields. By default, LiteDB convert all property names to same name in BSON document except _id field which is document identifier.
If you want query using Linq, you will use properties expressions. If you are using Query object class, you must use field name.
var result = col.FindById(123);
// or
var result = col.FindOne(x => x.Id == 123);
// or
var result = col.FindOne(Query.EQ("_id", 123));
Find using _id always returns 1 (or zero) document.
I figured out the problem with LiteDB, since I was using the property name of "Id", the BSON interpreted that as the "_id" of the JSON object, and merging their two values. I solve the issue by renaming the "Id" property to something else.

Filter Search query in Spring Mongo DB

In feed collection "likeCount" and "commentCount" are two column. I want to get all document where "likeCount" + "commentCount" greater than 100. How can I write the search filter query in Spring Mongo DB?
Below is my sample feed collection data.
{
"_id" : ObjectId("55deb33dcb9be727e8356289"),
"channelName" : "Facebook",
"likeCount" : 2,
"commentCount" : 10,
}
For compare single field we can write search query like :
BasicDBObject searchFilter = new BasicDBObject();
searchFilter.append("likeCount", new BasicDBObject("$gte",100));
DBCursor feedCursor = mongoTemplate.getCollection("feed").find(searchFilter);
Try this
db.collection.aggregate([{$project:{total:{'$add':["$likeCount","$commentCount"]}}},{$match:{total:{$gt:100}}}])
You would need to use the MongoDB Aggregation Framework with Spring Data MongoDB. In Spring Data the following returns all feeds with a combined likes and comments counts greater than 100, using the aggregation framework. :
Entities
class FeedsCount {
#Id String id;
String channelName;
long likeCount;
long commentCount;
long totalLikesComments;
//...
}
Aggregation
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
Aggregation agg = newAggregation(Feed.class,
project("id", "channelName", "likeCount", "commentCount")
.andExpression("likeCount + commentCount").as("totalLikesComments"),
match(where("totalLikesComments").gt(100))
);
//Convert the aggregation result into a List
AggregationResults<FeedsCount> groupResults
= mongoTemplate.aggregate(agg, FeedsCount.class);
List<FeedsCount> results = groupResults.getMappedResults();
In the code above, first create a new aggregation via the newAggregation static factory method to which you pass a list of aggregation operations. These aggregate operations define the aggregation pipeline of your Aggregation.
As a first step, select the "id", "channelName", "likeCount", "commentCount" fields from the input collection with the project operation and add a new field "totalLikesComments" which is a computed property that stores the sum of the "likeCount" and "commentCount" fields.
Finally in the second step, filter the intermediate result by using a match operation which accepts a Criteria query as an argument.
Note that you derive the name of the input-collection from the Feed-class passed as first parameter to the newAggregation-Method.

How do I sort by a property on a nullable association in Grails?

I'm trying to sort a table of data. I have the following domain (paraphrased and example-ified):
class Car {
Engine engine
static constraints = {
engine nullable: true // poor example, I know
}
}
class Engine {
String name
}
Here's the controller action that's handling the sort:
def myAction = {
def list = Car.findAll(params)
render(view: 'list', model: [list: list])
}
I provision some data such that there are several Cars, some with null engines and others with engines that are not null.
I attempt the following query:
http://www.example.com/myController/myAction?sort=engine.name&order=asc
The results from the query only return Car entries whose engine is not null. This is different from the results that would be returned if I only queried the association (without its property):
http://www.example.com/myController/myAction?sort=engine&order=asc
which would return all of the Car results, grouping the ones with null engines together.
Is there any way that:
I can get the query that sorts by the association property to return the same results as the one that sorts by only the association (with the null associations grouped together)?
I can achieve those results using the built-in sorting passed to list() (i.e. without using a Criteria or HQL query)
You need to specify LEFT_JOIN in the query, try this:
import org.hibernate.criterion.CriteriaSpecification
...
def list = Car.createCriteria().list ([max:params.max?:10, offset: params.offset?:0 ]){
if (params.sort == 'engine.name') {
createAlias("engine","e", CriteriaSpecification.LEFT_JOIN)
order( "e.name",params.order)
} else {
order(params.sort, params.order)
}
}
Remember to put engine.name as the property to order by in your list.gsp
<g:sortableColumn property="engine.name" title="Engine Name" />

Can ExecuteQuery return a DBML generated class without having it fetch all the information for that class?

I have a couple of DBML generated classes which are linked together by an id, e.g.
ClassA {
AID,
XID,
Name
}
ClassB {
AID,
ExtraInfo,
ExtraInfo2
}
In using something like db.ClassAs.Where(XID == x) and iterating through that result,
it ends up executing a query for each of the ClassAs and each of ClassBs, which is slow.
Alternatively, I've tried to use ExecuteQuery to fetch all the info I care about and have that return a ClassA. In iterating over that I end up with it doing the same, i.e. doing alot of individual fetches vs. just 1. If I store it in a ClassC (that is not associated with a DB entity) which has the fields of interest of both ClassA and ClassB, this query is much faster, but it's annoying b/c I just created IMO an unnecessary ClassC.
How can I still use ClassA, which associates to ClassB, and still use ExecuteQuery to run 1 query vs. A*B number of queries?
If you have associations you shouldn't need to use ExecuteQuery().
Here's an example using some imaginary Book Library context and anonymous types for the result:
var results =
Books
.Where(book => book.BookId == 1)
.Select(book =>
new
{
book.Name,
AuthorName = book.Author.Name, //Is a field in an associated table.
book.Publisher, //Is an associtated table.
});
EDIT: without anon types
var results =
Books
.Where(book => book.BookId == 1)
.Select(book =>
new BookResult()
{
BookName = book.Name,
AuthorName = book.Author.Name, //Is a field in an associated table.
Publisher = book.Publisher, //Is an associtated table.
});

Resources