What's an efficient way to retrieve chat messages from Realm? - java

I am using FCM to create a chat app, therefore both tokens and topics are being used. In my application I've created a POJO which extends RealmObject intended for storing the chat messages from different userIDs as well as the ones I've sent, both in private-chats and groups.
But what I can't understand is, how should I frame the Realm Query to retrieve the received messages and the messages I've sent to a UserID.
I'm thinking of trying:
RealmResults<ChatModel> results=mDatabase.where(TestChatModel.class)
.equalTo("sender",<Person with whom Im chatting's userID >)
.or()
.equalTo("receiver",<Person with whom Im chatting's userID >)
.and()
.equalTo("sender",<My userID >)
.or()
.equalTo("receiver",<My userID >
.sort("timestamp")
.findAll();
But that just seems very inefficient and messed up.
My POJO is:
public class TestChatModel {
private String chatMessage;
private String timestamp;
private String sender;
private String receiver;
private String topicName; // Set to NA is private-chat
private int isTopic; // Set to 0 for private-chat and Set to 1 for
// group
.
.
.
//Associated constructors and getters and setters
.
.
}
The community's help is much appreciated, thanks in advance !

Your query looks fine. All you can do is write down the query logic and then translate in into Realm query syntax. If your intention is to create a query with the criteria being that:
A specific person is either the sender or receiver, AND
The logged in user either the sender or receiver
Then that's probably the best way to do it. This assumes that you have ALL messages to and from everyone in the Realm; if you were to apply some other rule (e.g. that you only have messages including the logged in user in the Realm) then you could ditch clause 2, as this would be implied. An example of this would be if your API only provided messages for a logged in user (which seems like a reasonable scenario). That would improve efficiency and simplify the query.
In terms of other ways to improve efficiency, it's likely (although I have no direct evidence) that using a numerical ID for users rather than a string ID would allow for more efficient comparison and filtering in Realm. This would be preferable, but may depend on your API (again).
Finally, it's probably worth adding 'parentheses' to your query if it remains as above to ensure the operators are evaluated as you expect (i.e. the AND in the middle of the ORs). This can be accomplished with beginGroup and endGroup in the query (as described here).

Related

How can I delete rows in a database using a query?

There is a user with the attribute Role, by default TENANT, using a query we set him LANDLORD and in theHOUSE table he adds an apartment with various attributes: description, price, city_id and others. But suddenly this user wanted to remove himself from the status of LANDLORD, delete his apartments from our database and again become justTENANT, how in this case can I delete the information that he has apartments? How to do it, if he has apartments, then they need to be deleted, if not, then just change the user's status to TENANT?
At first there was an idea to assign a zero value, but it seemed strange to me if we just zeroed it out, because then the table will start to get cluttered. There is also a status option: ACTIVE or BANNED, but I don't like this option, because his apartment is still not needed.
The code looks like this:
#PutMapping ("/ {id}")
#PreAuthorize ("hasAuthority ('landlord: write')")
public void TenantPostAdd (#PathVariable (value = "id") Long id) {
User user = userRepository.findById (id) .orElseThrow ();
Role role = Role.TENANT;
user.setRole (role);
House house = houseRepository.findById (id) .orElseThrow ();
house ... // what's here
}
Full Code
To build this level of infrastructure, there are a lot of questions I would have to ask to recommend something. I'd want to see the current database schema as well. Your also requesting the ability to delete which can become problematic. You may want to consider leaving data if you believe that the customer may change roles again. That kind of information is based off of the terms of agreement.
Have you considered building something like this?
Absolute(Numeric) Mode
0 No Permission --- etc...
https://www.guru99.com/file-permissions.html
This could be a prepared statement issue with not the appropriate joins occurring in the statement. I believe you should take another look over your database schema.

Proper way to filter data from database [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
I'm new to spring framework so the questions might come up as silly.
I have a database with almost 5000 entries in it. I need to create a GET request which takes 5 parameters to filter the data. Depending on what parameters are present, the request will filter the data. I was able to do it but I don't think I am doing it efficiently. So here are my questions:
First, Which one is a better approach? Retrieving all data from database using repository.findAll() and then using stream + filter to filter out the data OR writing the query in JPA repository interface and then simply calling those methods? Which one would be more efficient?
Second, What is the best way to retrieve a huge amount of data? Like in my case, there are 5000 entries. So how should I retrieve them? I've read something about Pageable but not 100% sure. Is that the way to go or is there any other better option?
Any help appreciated. Thanks :)
For the first question is better to retrieve only required records from DB, instead of retrieve all entries and then filter them on Java .Writing the query in JPA repository one of the options , but also you can use CriteriaQuery to do this . CriteriaQuery given you more manipulate on fillture items on programmatically way . Also it help you with your second question .
Yes Pagination is one of approach , special for Web Applications . The Main idea of pagination is to dividing large records of data to smaller chunks (Pages) , user search for his record on first chuck (Page) then he/she will request the a second page if he/she did found it .
Below example summarize your two queries . In this example am trying to retrive/search on large number of orders .
Bean OrderSearchCriteria.java , use to identify filter parameter .
public class OrderSearchCriteria {
private String user ;
private Date periodFrom ;
private Date periodTo ;
private String status ;
private Integer pageLimit ;
private Integer page ;
private Integer offset ;
private String sortOrder ;
.....
}
Repository
public interface OrderRepository extends JpaRepository<Order, Integer> , JpaSpecificationExecutor<Order>{}
Below using CriteriaQuery to filter orders based on submitted criteria .
#Service
public class OrderServiceImpl implements OrderService{
......
#Override
public Page<Order> orderSearch(OrderSearchCriteria orderSearchCriteria) {
if (orderSearchCriteria.getPage() == null)
orderSearchCriteria.setPage(orderSearchCriteria.getOffset() / orderSearchCriteria.getPageLimit());
return orderRepository.findAll(OrderSearchSpecificaton.orderSearch(orderSearchCriteria) ,
PageRequest.of(orderSearchCriteria.getPage(), orderSearchCriteria.getPageLimit()));
}
private static class OrderSearchSpecificaton {
public static Specification<Order> orderSearch(OrderSearchCriteria orderSearchCriteria) {
return new Specification<Order>() {
private static final long serialVersionUID = 1L;
#Override
public Predicate toPredicate(Root<Order> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
if (!StringUtils.isEmpty(orderSearchCriteria.getUser()) && !orderSearchCriteria.getUser().toUpperCase().equals("ALL")) {
Join<Order, User> userJoin = root.join("user") ;
predicates.add(criteriaBuilder.equal(userJoin.get("name") ,orderSearchCriteria.getUser()));
}
if (!StringUtils.isEmpty(orderSearchCriteria.getStatus()) && !orderSearchCriteria.getStatus().toUpperCase().equals("ALL")) {
predicates.add(criteriaBuilder.equal(root.get("status") ,orderSearchCriteria.getStatus()));
}
if (orderSearchCriteria.getPeriodFrom() != null) {
predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("entryDate"), orderSearchCriteria.getPeriodFrom())) ;
}
if (orderSearchCriteria.getPeriodTo()!= null) {
predicates.add(criteriaBuilder.lessThan(root.get("entryDate"), orderSearchCriteria.getPeriodTo())) ;
}
if (!StringUtils.isEmpty(orderSearchCriteria.getSortOrder())) {
if (orderSearchCriteria.getSortOrder().toUpperCase().equals("DESC")) {
query.orderBy(criteriaBuilder.desc(root.get("entryDate"))) ;
}
else {
query.orderBy(criteriaBuilder.asc(root.get("entryDate"))) ;
}
}
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
};
}
}
Call orderSearch from Controller
#ResponseBody
#RequestMapping(path = "/order/search" , method = RequestMethod.POST)
public HashMap<String, Object> orderSearch(#RequestBody OrderSearchCriteria orderSearchCriteria) {
Page<Order> page = getOrderService().orderSearch(orderSearchCriteria) ;
HashMap<String, Object> result = new HashMap<>() ;
result.put("total", page.getTotalElements());
result.put("rows", page.getContent());
return result ;
}
I hope this can help you .
What is better depends on context. Only you know what is better in your context. Nevertheless I'd suggest you to consider following solution.
1) Use Spring Data JPA Specifications
You say that some of 5 parameters can be present, some not. I'd suggest you to use Spring Data JPA Specifications. Here is a good article and examples.
The idea is following. For each of your 5 parameters you create a specification. In this example these are methods customerHasBirthday() and isLongTermCustomer() in the class CustomerSpecifications.
Then you create a query dynamically, depending on what parameters are present:
if (parameter1 is present){
add specification 1 to the "where" clause
}
...
if (parameter5 is present){
add specification 5 to the "where" clause
}
Then calls findAll() using the resulting aggregated specification.
Of course other solutions are possible: You can build a JPQL Query as a string dynamically, depending on what parameters are present. Or you can dynamically build a native SQL query. But specifications have one more advantage: pageable queries in Spring accept only specifications.
2) Use Paging
If your application has only 2-3 users that send only a few requests per hour, then loading 5000 items per request might work well. But if all the results need to be rendered in browser, this can take a lot of resources on the client and can be a performance problem.
If you have more users that send more requests, then also on the server side CPU and RAM can be insufficient and you can face performance problems and, as a consequence, very long response time for users.
That's why I'd suggest you to use Paging. You can limit the number of elements in the response. Suppose you set page size to 100. Then each request will need less resources:
On database level: Instead of 5000 database will return only 100 elements, it will be better performance
Application will create from JDBC response only 100 Java objects instead of 5000 -> less memory and less CPU used
Application will have less overhead with converting Java objects to JSON, again less memory and less CPU
The response time will be better, because sending of 100 elements from application to the user takes less time than sending 5000 elements
Browser performance can be better. It depends on the client logic. In case client application is not smart and renders every response element, this will be a higher performance, because rendering of 100 elements will be faster than rendering of 5000 elements.
There are many tutorials about paging, do one or two that you like.

Count specific enum values with JPA rather than manually

I have a list of status enum values which I am currently iterating over and using a basic counter to store how many in my list have the specific value that I am looking for. I want to improve greatly on this however and think that there may be a way to use some kind of JPA query on a paging and sorting repository to accomplish the same thing.
My current version which isn't as optimized as I would like is as follows.
public enum MailStatus {
SENT("SENT"),
DELETED("DELETED"),
SENDING("SENDING"),
}
val mails = mailService.getAllMailForUser(userId).toMutableList()
mails.forEach { mail ->
if (mail.status === MailStatus.SENT) {
mailCounter++
}
}
With a paging and sorting JPA repository is there some way to query this instead and get a count of all mail that has a status of sent only?
I tried the following but seem to be getting everything rather than just the 'SENT' status.
fun countByUserIdAndMailStatusIn(userId: UUID, mailStatus: List<MailStatus>): Long

How to write the read rule for a Firebase Firestore Many to Many relationship

I have the following data structure in Firebase Firestore to represent a many to many relationship between clients and users:
Clients
clientId1 {
users (object): {
userId1: true
userId2: true
}
}
clientId2 {
users (object): {
userId1: true
}
}
I query it on Android using the following query:
db.collection("clients").whereEqualTo("users."+uid, true);
For userId2, the query should only return clientId1.
If I set the rule to (allow read: if true;) when I execute the query above I get the correct clients returned.
I would also like to set up a database rule to prevent userId2 from seeing clientId2.
I tried this rule but I get no results returned:
match /clients/{clientId} {
//Allow read if the user exists in the user collection for this client
allow read: if users[request.auth.uid] == true;
}
I also tried:
match /clients/{clientId} {
//Allow read if the user exists in the user collection for this client
allow read: if resource.data.users[request.auth.uid] == true;
}
But neither of the above rules returns any clients.
How do I write the rule?
I am going to answer my own question as I was just doing something silly.
My data structure is fine and the correct syntax for my rule is this one:
match /clients/{clientId} {
//Allow read if the user exists in the user collection for this client
allow read: if resource.data.users[request.auth.uid] == true;
}
Given this:
Cloud Firestore evaluates a query against its potential result set
instead of the actual field values for all of your documents. If a
query could potentially return documents that the client does not have
permission to read, the entire request fails.
This Android query does correctly implement the right filter for the rule:
db.collection("clients").whereEqualTo("users."+uid, true);
I am yet to implement my adapter properly. I wanted to see if I could get the correct data structure / rules / query working first. I was calling it from another listener that was listening on the entire client collection (which fails the rule) and therefore this query was not being called. Earlier when I set the rule to (allow read: if true;) the initial listener was executing my query and returning the correct results. This lead me to believe my rule was incorrect, when it wasn't.
As per the official documentation regarding Firestore Security Rules:
When writing queries to retrieve documents, keep in mind that security rules are not filters—queries are all or nothing. To save you time and resources, Cloud Firestore evaluates a query against its potential result set instead of the actual field values for all of your documents. If a query could potentially return documents that the client does not have permission to read, the entire request fails.
So you cannot filter the documents that exist in your database using security rules.

Getting all users with a Role in Liferay

I'm new to Liferay development in general, so feel free to point out if I'm going about stuff totally the wrong way.
I'm trying to get a DynamicQuery object of all users within a certain group (I'll use this object to further filter another query I'll do against the message board). The User interface seems to have a roleIds property that I might be able to use, since I already know the roleId I'm interested in. But I can't find the proper way to query if roleIds contains a certain value.
Any ideas on what I want to do?
PS: I would have the exact SQL query I could ask directly, but I'd rather use Liferay's own connection pool, without needing to do some weird ext project thingy.
You don't need a DynamicQuery. These are the methods you are looking for in the classes that Dirk points out:
long[] UserServiceUtil.getRoleUserIds(long roleId)
or
long[] UserLocalServiceUtil.getRoleUserIds(long roleId)
List<User> UserLocalServiceUtil.getRoleUsers(long roleId)
Remember that the methods in the classes XXXLocalServiceUtil are not checking the permissions of the current user.
EDIT: If you are looking for all users with a given role within a given community:
long companyId= _X_; //Perhaps CompanyThreadLocal.getCompanyId() if you don't have it anywhere else?
Role role=RoleLocalServiceUtil.getRole(companyId, "Example Role");
Group group=GroupLocalServiceUtil.getGroup(companyId, "Example Community");
List<UserGroupRole> userGroupRoles = UserGroupRoleLocalServiceUtil.
getUserGroupRolesByGroupAndRole(groupId, role.getRoleId());
for(UserGroupRole userGroupRole:userGroupRoles){
User oneUser=userGroupRole.getUser();
}
The easiest way to access liferays own objects is by using the XXXServiceUtil classes (e.g. RoleServiceUtil.getUserRoles(userId)). Thus you rarely have to deal with any SQL directly. Either the RoleServiceUtil or UserServiceUtil might have what you need.
The roles of an Organizations are stored in the table UserGroupRole, so if you want to get the owner of an Organization you must use the following code:
boolean isOrgOwner =
UserGroupRoleLocalServiceUtil.hasUserGroupRole(
usr.getUserId(),
this.currentOrganization.getGroupId(),
RoleConstants.ORGANIZATION_OWNER);
If you want to retrieve all the Organization Owners of an organization:
List<User> administrators = new LinkedList<>();
List<UserGroupRole> allOrganizationAdministrators =
UserGroupRoleLocalServiceUtil.getUserGroupRolesByGroupAndRole(
this.currentOrganization.getGroupId(), roleId);
for (UserGroupRole userGroupRoleTemp : allOrganizationAdministrators) {
administrators.add(userGroupRoleTemp.getUser());
}
Cheers!

Categories

Resources