Firebase data retrieval java - java

{
"Account1" :
{
Push_key(): { Carplate: "ABC1234" }
Push_key(): { Carplate: "ABC" }
Push_key(): { Carplate: "A" }
}
}
This is how the database looks like.
I would like to retrieve the third data which contains "A" alone ONLY.
I am using startAt() and endAt() for data retrieval:
Query query = ref.child("Account1").orderByChild("Carplate").startAt("A").endAt("A"+"\uf8ff");
But it returns all 3 records. (I think its due to they are all started at "A".)
Need help! Please!

You should look at the equalTo() method for this (from the doc):
The equalTo() method allows you to filter based on exact matches. As is the case with the other range queries, it will fire for each matching child node.
To adapt it to your query you might try:
Query query = ref.child("Account1").orderByChild("Carplate").equalTo("A");

Related

Couchbase Lite - use coalesce / ifNullOrMissing in order by clause

I have documents stored in a couchbase lite database. I use the query builder to request these documents in Java.
I would like to to order the retrieved documents given two properties: if one is missing, I'd like to use the value of another for the ordering.
For example, considering these data stored in the couchbase lite:
{
"firstname":"Russell",
"lastname":"Macdonald"
},
{
"firstname":"Brielle"
"birthname":"Vaughn"
"lastname":"Bates"
},
{
"firstname":"Molly"
"birthname":"Arellano"
"lastname":"Nichols"
}
I would like to order by birthname. But if the birthname is missing, the lastname should be used instead. The resulting order would be:
Molly Arellano (married Nichols)
Russell Macdonald
Brielle Vaughn (married Bates)
I tried passing the two successive properties to the order by clause. But, unsurprisingly, it did not work:
List<Result> results = select(all())
.from(database(myDatabase))
.orderBy(Ordering.property("birthname"), Ordering.property("lastname"))
.execute().allResults()
I don't think you can do this with the QueryBuilder interface. SQL+, however, has a function: "IFMISSINGORNULL(arg1, arg2)" whose value is its first argument, if ISMISSINGORNULL is false for that argument and its second argument otherwise. You should be able to use the query:
"select * from _ order by IFMISSINGORNULL(birthname, lastname)"
FWIW, the ResultSet produced by Query.execute() should be closed. It is AutoClosable so you might do something like this:
final Query query = db.createQuery("select * from _ order by IFMISSINGORNULL(birthname, lastname)");
try (ResultSet results = query.execute()) {
// parse the results...
}

How to sort a particular page number in Spring Data JPA?

I have this paging problem where when I try to sort a table by field header on a particular page number, PageRequest.of(page-1, 10, sort) is sorting the entire table, not on a particular page. Thus, what record is returned in that page is different from the previous record before sorting.
Code:
#Override
public Page<User> getPageAndSort(String field, String direction, int page) {
Sort sort = direction.equalsIgnoreCase(Sort.Direction.ASC.name())
? Sort.by(field).ascending()
: Sort.by(field).descending();
Pageable pageable = PageRequest.of(page-1, 10, sort);
return userRepo.findAll(pageable);
}
For example. I want to sort only in page 1 by id. Returning a sorted record from page 1. The rest of the pages or entire records shouldn't
be affected.
Thank you.
Edit:
I have a workaround in this problem. After getting a page from:
Page<User> page = userService.findPage(currentPage);
I get the page.getContent() List and then pass to method sortList:
userService.sortList(new ArrayList<>(page.getContent()), field, sortDir)
sort implementation:
public ArrayList<User> sortList(ArrayList<User> users, String field, String direction) {
users.sort((User user1, User user2) -> {
try {
Field field1 = user1.getClass().getDeclaredField(field);
field1.setAccessible(true);
Object object1 = field1.get(user1);
Field field2 = user2.getClass().getDeclaredField(field);
field2.setAccessible(true);
Object object2 = field2.get(user2);
int result = 0;
if (isInt(object1.toString())) {
result = Integer.parseInt(object1.toString()) - Integer.parseInt(object2.toString());
} else {
result = object1.toString().compareToIgnoreCase(object2.toString());
}
if (result > 0) {
return direction.equalsIgnoreCase("asc") ? 1 : -1;
}
if (result < 0) {
return direction.equalsIgnoreCase("asc") ? -1 : 1;
}
return 0;
} catch (Exception e) {
Log.error(e.toString());
return 0;
}
});
return users;
}
With this work around. I successfully sorted a particular page by its column header without affecting the rest of pages. But it's not standard though as it doesn't use PageRequest.of() from Spring Data JPA and I recommend testing the code and review it thoroughly.
I think an if condition could solve the problem. Create Pageable instance with respect to the condition.
#Override
public Page<User> getPageAndSort(String field, String direction, int page) {
Sort sort = direction.equalsIgnoreCase(Sort.Direction.ASC.name())
? Sort.by(field).ascending()
: Sort.by(field).descending();
Pageable pageable = (page == 1)?PageRequest.of(page-1, 10, sort)
:PageRequest.of(page-1, 10);
return userRepo.findAll(pageable);
}
References : https://www.baeldung.com/spring-data-jpa-pagination-sorting#:~:text=We%20can%20create%20a%20PageRequest%20object%20by%20passing,%280%2C%202%29%3B%20Pageable%20secondPageWithFiveElements%20%3D%20PageRequest.of%20%281%2C%205%29%3B
I think this helps.
I don't think there is an easy way to make this kind of sorting in the database and since you are dealing with a single page which is memory anyway since you render it to the UI, I would just sort it in memory.
Alternatively you can go with a custom SQL statement structured like this:
SELECT * FROM (
SELECT * FROM WHATEVER
ORDER BY -- sort clause defining the pagination
OFFSET ... LIMIT ... -- note that this clause is database dependent.
) ORDER BY -- your sort criteria within the page goes here
You'll have to construct this SQL statement programmatically, so you can't use Spring Datas special features like annotated queries or query derivation.
I'm not sure, I got your question, but if you want a certain sorted page, the db should definitely create the query plan, sort all the data and return you a certain offset (Page) of the sorted data.
It's impossible to get a sorted page without sorting the whole data.
I believe you want to sort data only on a given page, this is difficult to manage with database query which probably will sort whole data and would give you nth page.
I would suggest to do a reverse on a given page after retrieving with same order.
Retrieve the nth page with from database with always asc.
Depending on direction do a reverse if needed.
This should be faster than relying on database for sort operation.

How to use List<String> params in Restrictions.like

I use List objects in Restrictions.in methods, now I must use this case at Restrictions.like
But Restrictions.like doesn't receive List param. How Can I solve this problem?
My code is bottom:
public void setPhones(Set<String> phones) {
this.phones.addAll(phones);
if (!this.phones.isEmpty()) {
aliases.add(new QueryAlias("profile", "profile"));
criterions.add(Restrictions.like("profile.phone", this.phones));
}
}
Correcting my prior (and now edited) answer
According to documentation (https://docs.jboss.org/hibernate/orm/5.4/javadocs/org/hibernate/criterion/Restrictions.html) it seems you don't have a direct way to do this.
You can try iterating your List, then create a List of Restriction.like for each of your phones then converting this list into an array to use into a Restrictions.or:
public void setPhones(Set<String> phones) {
this.phones.addAll(phones);
if (!this.phones.isEmpty()) {
// Creates a list to store criterions
List<Criterion> criterionsPhoneNumbers = new ArrayList<>();
// For each number searched, it creates a criterion with a "Like Restriction" adding to criterionsPhoneNumbers List.
// Pay attention to match mode (in raw sql it'll be a "like" using %phoneNumber% - check the generated SQL by hibernate).
// You can change this to suit your needs.
for (String number : numbers) {
aliases.add(new QueryAlias("profile", "profile"));
criterionsPhoneNumbers.add( Restrictions.like("number", number, MatchMode.ANYWHERE) ) ;
}
// Here criterionsPhoneNumbers is being converted to array then added to the criteria with "Or Restriction"
criteria.add(Restrictions.or( criterionsPhoneNumbers.toArray(new Criterion[restrictionsPhoneNumbers.size()]) ));
}
}
My prior answer was wrong because adding each phone number as a Restriction.like (only) wasn't enough and was converted to a sql with a logical 'and' in 'where' clause. As I hadn't tested then I couldn't see the error.
I had implemented then I saw the error.
My apologies.

How to return a list of results in jOOQ Mocked Data

I'm using jOOQ's MockDataProvider to mock calls to the database. I've figured out how to return a single record using the information here: https://blog.jooq.org/2013/02/20/easy-mocking-of-your-database/
However, I want to return a list of results not just a single record for my query. How do I do that?
I can use the following (from the above link) to return a single result:
return new MockResult[] {
new MockResult(1, result)
};
However, I cannot figure out how to add multiple results, all of the constructors for MockResult only take a single result. Any hints? Am I missing something obvious?
For example if I query all bicycles that are road bikes:
SELECT * FROM bicycles WHERE type = "road";
how do I return a list of 10 bicycles instead of just one?
I can use the following (from the above link) to return a single result
But that's already it. You return a single result with several records. The result you pass to that MockResult constructor could look like this:
var result = ctx.newResult(BICYCLES.COL1, BICYCLES.COL2);
result.add(ctx.newRecord(BICYCLES.COL1, BICYCLES.COL2).values(1, 2));
result.add(ctx.newRecord(BICYCLES.COL1, BICYCLES.COL2).values(3, 4));
...

How to query on inherited classes with querydsl

I use querydsl to query on a mongodb. As allowed by mongodb, in several cases I store objects of different types in the same collection.
For instance, in my data model I have:
interface Notification {
NotificationType getType(); // EMAIL, SMS etc.
}
interface EmailNotification extends Notification {
Set<User> getRecipients();
}
Now I want to query for Notifications of any kind (not only EmailNotification), but in the case I have EmailNotifications, I want to filter on some recipient.
I tried this code (doesn't work):
final QNotification notification = QNotification.notification;
final BooleanBuilder typesPredicate = new BooleanBuilder();
// "recipientEmails" and "recipientPhones" are provided parameters (lists of String)
typesPredicate.or(notification.type.eq(NotificationType.EMAIL)
.and(notification.as(QEmailNotification.class).recipients
.any().email.in(recipientEmails)));
typesPredicate.or(notification.type.eq(NotificationType.SMS)
.and(notification.as(QSMSNotification.class).recipients
.any().phoneNumber.in(recipientPhones)));
notificationPersister.query(Notification.class).where(typesPredicate);
It doesn't throw any error or exception, but I don't get the expected result (actually I don't get any result) because the generated mongo query is wrong, and I can't get how to make it right.
Generated query is like:
{
"$and":[
// ...
{
"$or":[
{
"type":"EMAIL",
"notification.recipients.email":{
"$in":[
"a#b.com"
]
}
},
// ...
]
}
]
}
And the issue lies in the key "notification.recipients.email": it should be just "recipients.email".
Why does "notification.as(QEmailNotification.class).recipients" translates to "notification.recipients", and how can I make it as expected ?
Note that the following would work:
notificationPersister.query(EmailNotification.class).where(
QEmailNotification.eMailNotification.recipients.any().email.in(recipientEmails));
But then I'm forced to run 1 query per inherited class, which is not efficient.
As Timo said: Fixed in querydsl via github.com/querydsl/querydsl/pull/1428

Categories