Realm.io Query with GroupBy - java

I want to group some accounts by month, can i do this with Realm.io?
public class Account extends RealmObject {
.....
private Date date;
}
RealmResults accounts = realm.where(Account.class)
.beginGroup()
.equalTo("date", "MONTH(date)")//<----- wrong code
.endGroup()
.findAll();
thanks

Realm doesn't support GroupBy yet. Also be aware that beginGroup() is actually the same as parentheses. So your query is actually interpreted as :
// SQL pseudo code
SELECT * FROM Account WHERE (date = MONTH(date))
In Realm you would have to do something like this to select a single month:
// Between is [ monthStart, monthEnd ]
Date monthStart = new GregorianCalendar(2015, 5, 1).getTime();
Date monthEnd = new GregorianCalendar(2015, 6, 1).getTime() - 1;
accounts = realm.where(Account.class).between("date", monthStart, monthEnd).findAll();
or something like this to detect when a month changes
// pseudo code. You might want to use Calendar instead
accounts = realm.where(Account.class).findAllSorted("date")
Iterator<Account> it = accounts.iterator();
int previousMonth = it.next().getDate().getMonth();
while (it.hasNext) {
int month = it.next().getDate().getMonth();
if (month != previousMonth) {
// month changed
}
previousMonth = month;
}

Related

How to filter day, month or both using CriteriaBuilder Java

someone knows how to create a filter using CriteriaBuilder?
I'm building an api and I need to create a filter for a list method. In SQL Server database, the column is a smalldatetime and the SQL Query that solve my problem is like:
SELECT * from TABLE
where DAY(COLUMN) LIKE 10 AND MONTH(COLUMN) LIKE 5
I have a DTO class that I use to filter in my request:
public class MyDTOFilter {
private Integer day;
private Integer month;
}
For example, if a give some day = 5, I would like this response:
"2020-01-05"
"2020-02-05"
"2019-10-05"...
If a give both, like month = 1 and day = 10, I would like this response:
"2020-01-10"
"2019-01-10"
"2018-01-10"...
How can I do this using CriteriaBuilder in Java?
Thank you all.
As CriteriaBuilder do not have specialized database functions you need to call this functions by name:
// DAY(COLUMN) returns an int
criteriaBuilder.function("DAY", Integer.class, "COLUMN")
// or, with JPA metamodel
criteriaBuilder.function("DAY", Integer.class, Entity_.column)
The result should be something like this:
public List<LocalDate> findDates(int day, int month) {
CriteriaQuery<LocalDate> query = criteriaBuilder.createQuery(LocalDate.class);
Root<Entity> root = query.from(Entity.class);
query.select(root.get(Entity_.column));
List<Predicate> predicates = new ArrayList<>();
ParameterExpression<Integer> dayParameter = null;
ParameterExpression<Integer> monthParameter = null;
if (day > 0) {
dayParameter = criteriaBuilder.parameter(Integer.class);
predicates.add(
criteriaBuilder.equal(
criteriaBuilder.function("DAY", Integer.class, root.get(Entity_.column)),
dayParameter ));
}
if (month > 0) {
monthParameter = criteriaBuilder.parameter(Integer.class);
predicates.add(
criteriaBuilder.equal(
criteriaBuilder.function("MONTH", Integer.class, root.get(Entity_.column)),
monthParameter ));
}
if (!predicates.isEmpty()) {
query.where(criteriaBuilder.and(
predicates.toArray(new Predicate[predicates.size()])));
}
TypedQuery<GregorianCalendar> typedQuery = getEntityManager().createQuery(query);
if (dayParameter != null) {
typedQuery.setParameter(dayParameter, day);
}
if (monthParameter != null) {
typedQuery.setParameter(monthParameter, month);
}
return typedQuery.getResultList();
}

date range search with respect to number of chunk size

I wanted to do a date range search in java suppose I wanted to search from 10-22-2019 to the present date.
But the question is to do the date range search in the chunk size of two weeks(consider this can vary but in form weeks) for eg here start date will 10-22-2019 but the end date will start date + 2 weeks added to it after the date range search is done for this and taking the result. Now the new start date should be were the previous date range search ended. and the end date should be now 2 weeks from the new start date again the search and this keeps on until I get till the present date.
public static IEnumerable<Tuple<DateTime, DateTime>> SplitDateRange(DateTime start, DateTime end, int dayChunkSize)
{
DateTime chunkEnd;
while ((chunkEnd = start.AddDays(dayChunkSize)) < end)
{
yield return Tuple.Create(start, chunkEnd);
start = chunkEnd;
}
yield return Tuple.Create(start, end);
}
Got this from one of the answers but have trouble in implementing in my situtation.
Simple iterative solution :
LocalDate start = LocalDate.parse("2019-10-22");
LocalDate end = LocalDate.now();
LocalDate chunckStart = start;
while (chunckStart.plusDays(15).isBefore(end)) {
doTheThing(chunckStart, chunckStart.plusDays(15));
chunckStart = chunckStart.plusDays(16);
}
doTheThing(chunckStart, end);
You can try it here.
try:
public static Iterator<Pair<LocalDate, LocalDate>> splitDataRange(LocalDate start, LocalDate end, int dayChunkSize) {
return new Iterator<Pair<LocalDate, LocalDate>>() {
private LocalDate chunkStart = start;
private LocalDate chunkEnd = start.plusDays(dayChunkSize);
#Override
public boolean hasNext() {
return end.compareTo(chunkEnd) > 0;
}
#Override
public Pair<LocalDate, LocalDate> next() {
Pair<LocalDate, LocalDate> chunk = new Pair<>(chunkStart, chunkEnd);
chunkStart = chunkEnd;
chunkEnd = chunkEnd.plusDays(dayChunkSize);
return chunk;
}
};
}
public static void main(String[] args) {
Iterator<Pair<LocalDate, LocalDate>> periodIterator = splitDataRange(LocalDate.of(2019, 3, 1),
LocalDate.of(2019, 5, 1), 20);
while (periodIterator.hasNext()) {
Pair<LocalDate, LocalDate> startEnd = periodIterator.next();
System.out.println(String.format("from %s to %s"
, startEnd.getValue0(), startEnd.getValue1()));
}
}
The Pair api is from:
<dependency>
<groupId>org.javatuples</groupId>
<artifactId>javatuples</artifactId>
<version>1.2</version>
</dependency>
Here's a solution using Streams and LocalDate:
LocalDate start = LocalDate.of(2020, 2, 28);
LocalDate end = LocalDate.now();
int step = 7;
start
.datesUntil(end, Period.ofDays(step))
.map(date -> {
LocalDate proposedEnd = date.plusDays(step);
LocalDate chunkEnd = proposedEnd.compareTo(end) > 0 ? end : proposedEnd;
return new SimpleEntry<>(date, chunkEnd);
})
.forEach(chunk -> System.out.println(chunk.getKey() + " until " + chunk.getValue()));
It generates the same output as the corresponding C# program.
The datesUntil method requires Java 9. Otherwise, if you're using Java 8, then you could use a helper method instead:
public static Stream<LocalDate> datesUntil(LocalDate from, LocalDate toExclusive) {
long daysBetween = ChronoUnit.DAYS.between(from, toExclusive);
return Stream.iterate(from, t -> t.plusDays(1))
.limit(daysBetween);
}

Perform select of data from multiple tablea using JDBC template

I need to perfom a select by date from my DB in my spring boot webapp. What I have so far is a list of sport competitions and there respective informations.
Problem : I can not figure out how my select query convert my String type (dateFrom = '2017-05-02' and dateTo = '2017-05-06') to date like '2017-02-12' in the ?
Alos how to fill my RowMapper with more then one date in some competition which have more then one date.
My data base schema:
CREATE TABLE competition (
competition_id integer PRIMARY KEY,
nom varchar(128) NOT NULL,
);
CREATE TABLE date (
id integer PRIMARY KEY,
date_time timestamptz,
competition_id integer REFERENCES competition (competition_id)
);
Json data:
{
"id": "420",
"name": "SOCCER",
"dates": [
"2016-05-12T03:00:00.000Z"
"2016-05-12T04:00:00.000Z"
"2016-05-12T05:00:00.000Z"
]
},
{
"id": "220",
"name": "BASKETBALL",
"dates": [
"2016-05-12T03:00:00.000Z"
"2016-05-12T04:00:00.000Z"
]
}
My competition Class:
public class Competition{
private int id;
private String name;
private String[] dates;
// setters ... getters
}
My RowMapper Class:
public class RowMapper implements RowMapper
{
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Competition competition = new Competition();
competition.setId(rs.getInt("id"));
competition.setName(rs.getString("name"));
competition. // How to fill dates
return competition;
}
}
Function to select data :
private static final String SELECT_STMT =
" select * from competition INNER JOIN date ON
+ " competition.competition_id = date.competition_id"
+ " WHERE date(date.date_time) BETWEEN ? AND ?"
;
public List<Competition> findByOptionsAll(String dateFrom, String dateTo ){
List<Competition> competitions = jdbcTemplate.query(SELECT_STMT, new
RowMapper(), dateFrom, dateTo);
return competitions ;
}
Date converting
Right now you have all dates as a String both in your DB and domain model. To convert strings to date you need a date formatter:
private static final String DATE_FORMAT = "dd-MM-yy";
// parsing date; Note you should handle ParseException
java.util.Date date = new SimpleDateFormat(DATE_FORMAT).parse(dateAsString);
// converting date to string
String dateAsString = new SimpleDateFormat(DATE_FORMAT).format(date);
Note that SimpleDateFormat is not thread-safe so it's a good practice to have static final String DATE_FORMAT instead of static final DateFormatter
Converting date and time is tricky in some cases (what about time zone? java.util.Date vs joda.time vs LocalDate from Java 8) but out of scope. I suggest use LocalDate if possible just because it's a new way without old issues.
Mapping
You have two entities in your DB (Competition and Date-of-competition) and only one class Competition in your domain model. Most probably, later you'll want to add additional info to the Date-of-competition (boolean finished, cancelled, Score etc) so it's a good idea to create CompetitionInstance class right now.
Since you have One-to-Many relationship you have to write some additional stuff to map objects. Normally that's what an ORM like Hibernate do istead of you. First, add a 'GROUP BY competition_id' in your sql statement.
Then use RowSetExtractor instead of RowMapper as described here:
private static final class CompetitionMapExtractor implements ResultSetExtractor<List<Competition>> {
#Override
public List<Competition> extractData(ResultSet rs) throws SQLException {
List<Competition> result = new ArrayList<>(rs.getCount());
int previousCompetitionId = NEVER_EXIST; // normally -1 is good enough
while (rs.next()) {
// we have some dates with the same competition_id
// dates are grouped thanks to GROUP BY clause
if ( rs.getInt("id") != previousCompetitionId) {
Competition currentCompetition = new Competition(rs.getInt("id"),
rs.getString("name");
/* I prefer constructor initializers "o = new O(propertyValue)"
instead of snippet "o = new O(); o.setProperty(value)"
*/
result.add(currentCompetition);
previousCompetitionId = currentCompetition.getid();
} else {
currentCompetition.addDate(new CompetitionInstance(rs.getString("date")));
}
}
return result;
}
I suppose Competition has method public void addDate(String date) which simply add a new CompetitionInstance to a list.
Update:
1.
column name in DB and in MapExtractor is different. I prefer to change the query:
SELECT c.id, c.name, d.date_time as date
from competition c
INNER JOIN date d ON c.competition_id = d.competition_id
WHERE date(d.date_time) BETWEEN ? AND ?"
2. I can't reproduce issues you have with date. Most probably you mixed up java.util.Date, java.sql.Date and java.sql.Timestamp - this is a common mistake. There are many answers already, probably you could find one of them useful.

Comparing Dates in Google Sheets

I am new to Java and to Google Script Editor. I have a custom CRM spreadsheet in google sheets, and would like to set up reminder emails based on regularly scheduled follow-up dates. I'm having trouble with the code. I think the trouble may be due to the fact that I'm trying to compare a date to a string, but I can't figure out how to get it to work.
The goal is to send off an email when the date for follow-up matches today's date. The date for follow-up is calculated based on a formula.
Even when the log reads:
[16-07-28 13:38:06:549 PDT] Date is Thu Jul 28 2016 00:00:00 GMT-0700 (PDT)
[16-07-28 13:38:06:549 PDT] Today is Thu Jul 28 2016 00:00:00 GMT-0700 (PDT)
My If statement if (date == todayFull) doesn't work. Here's the code:
function sendEmails() {
var ss = SpreadsheetApp.openById("number");
var sheet = ss.getSheetByName("Script");
var startRow = 2; // First row of data to process
var lastRow = sheet.getLastRow();
var lastCol = sheet.getLastColumn();
// Fetch the range of cells
var dataRange = sheet.getRange(2, 1, lastRow, lastCol);
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var date = row[32];
var todayFull = new Date();
todayFull.setHours(0);
todayFull.setMinutes(0);
todayFull.setSeconds(0);
Logger.log("Date is "+date);
Logger.log("Today is "+todayFull);
if (date == todayFull) {
Logger.log("This is a test. The Date is Today");
// var emailAddress = row[28]; // Email column
// var groupName = row[3]; // Group Name column
// var subject = "Follow up with this group right now!";
// MailApp.sendEmail(emailAddress, subject, groupName);
};
};
}
Thanks for the help. The first answer ended up working most of the way. Using .getDate() helped, but I also had to add arguments for month and year. Here's the code I ended up with:
function sendEmails() {
var ss = SpreadsheetApp.openById("");
var sheet = ss.getSheetByName("");
var startRow = 4; // First row of data to process
var lastRow = sheet.getLastRow(); //Get the last row of data to be processed
var lastCol = sheet.getLastColumn();
var dataRange = sheet.getRange(2, 1, lastRow-3, lastCol);
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var date2 = new Date(row[24]); // creates a new Date (date in string) object
var todayFull2 = new Date(); // creates a new Date (now) object
if (date2.getDate() == todayFull2.getDate() && date2.getMonth() == todayFull2.getMonth() && date2.getYear() == todayFull2.getYear()) {
etc
You're comparing two different data types:
var date = row[32]; // reads in a String object
var todayFull = new Date(); // creates a new Date (now) object
...
if (date == todayFull) { // compares Date with String
...
}
You might be better off creating the Date object when you read the value from your Sheet, and then comparing the actual dates in milliseconds (at time 00:00:00:00 of given date) of those Date objects, as you appear to be intending to do:
var date = new Date(row[32]); // creates a new Date (date in string) object
var todayFull = new Date(); // creates a new Date (now) object
todayFull.setHours(0,0,0,0) // sets time to midnight of current day
...
// compare Date with Date
if (date.getTime() == todayFull.getTime()) {
...
}
See MDN's excellent documentation on Javascript's Date.

How to count the number of events on a specific calendar for today only?

I'm trying to build a simple example application that counts the number of events on a specific calendar (I know the CALENDAR_DISPLAY_NAME of the Calendar) for today only.
I believe I need to query todays events with CalendarContract, and then count the number of rows in the cursor?
Is that correct? What would be the minimal, most efficient way to do the query on the single calendar, and have it return only the minimal possible set of data (event id only?) ?
This is the solution I arrived at:
First find the Calendar ID from the known name.
Then:
private Cursor getEventInstancesCursor() { // Query for a list of GQueues Calendar instances today
// Set Calendar variable for start and end of today.
Calendar startOfDay = Calendar.getInstance();
int thisYear = startOfDay.get(Calendar.YEAR);
int thisMonth = startOfDay.get(Calendar.MONTH);
int thisDay = startOfDay.get(Calendar.DAY_OF_MONTH);
startOfDay.set(thisYear, thisMonth, thisDay, 0, 0, 0);
long startMillis = startOfDay.getTimeInMillis();
Calendar endOfDay = Calendar.getInstance();
endOfDay.set(thisYear, thisMonth, thisDay, 23, 59, 59);
long endMillis = endOfDay.getTimeInMillis();
long GQueuesCalID = getGQueuesCalID();
Cursor eventsCursor = null;
ContentResolver eventsCR = getContentResolver();
String selection = CalendarContract.Instances.CALENDAR_ID + " = ?";
String calIDString = String.valueOf(GQueuesCalID);
String[] selectionArgs = {calIDString};
try {
return eventsCursor = eventsCR.query(
CalendarContract.Instances.CONTENT_URI.buildUpon()
.appendPath(Long.toString(startMillis))
.appendPath(Long.toString(endMillis))
.build(),
InstancesQuery.PROJECTION,
selection,
selectionArgs,
null);
} catch (Exception e) {
Log.e(TAG, "Error querying calendar API", e);
return null;
}
}
Then just count the cursor rows.

Categories