Speed of search operation in a Realm database - java

This is the model for my RealmObject class :
public class ARDatabase extends RealmObject
{
#PrimaryKey
private String uid;
private String namex;
private String desc;
private boolean isVideo;
private boolean isDeleted;
private String urlImg;
private String urlApp;
private int updates;
private boolean isDownloaded;
private String location;
public ARDatabase(){}
public String getUid()
{
return uid;
}
public void setUid(String uid)
{
this.uid = uid;
}
public String getNamex()
{
return namex;
}
public void setNamex(String namex)
{
this.namex = namex;
}
public String getDesc()
{
return desc;
}
public void setDesc(String desc)
{
this.desc = desc;
}
public boolean getIsVideo()
{
return isVideo;
}
public void setIsVideo(boolean isVideo)
{
this.isVideo = isVideo;
}
public boolean getIsDeleted()
{
return isDeleted;
}
public void setIsDeleted(boolean isDeleted)
{
this.isDeleted = isDeleted;
}
public String getUrlImg()
{
return urlImg;
}
public void setUrlImg(String urlImg)
{
this.urlImg = urlImg;
}
public String getUrlApp()
{
return urlApp;
}
public void setUrlApp(String urlApp)
{
this.urlApp = urlApp;
}
public int getUpdates()
{
return updates;
}
public void setUpdates(int updates)
{
this.updates = updates;
}
public boolean getIsDownloaded()
{
return isDownloaded;
}
public void setIsDownloaded(boolean isDownloaded)
{
this.isDownloaded = isDownloaded;
}
public String getLocation()
{
return location;
}
public void setLocation(String location)
{
this.location = location;
}
}
And this is how I search for the uid from my default realm:
final ARDatabase db = mRealm.where(ARDatabase.class).equalTo("uid",imageTitles.get(result)).findFirst();
Now, the question is: Considering I have 10-100 objects inside my realm, how fast would the search be?
The use case is for an image recognition app. When the app recognizes an image it returns the uid, and based on the uid I need to provide an overlay on the screen with the information associated with the uid ASAP.
Now since I have around 10-100 objects, would a linear search O(n) or a generic binary search O(log n) would be faster than the Realm search I've used above? Or is it possible to tweak Realm to get faster results? (in case it's not performing the fastest way now).

Doing the search in Realm will always be faster since you can execute the entire search inside the C++ core. Doing the search yourself will mean you occur the overhead of going back and forth between Java and C++.
The only requirement for doing fast searching for single elements is that you have an #Index annotation on the field, but in your case you already have #PrimaryKey which automatically applies the #Index annotation as well.
So your query is as fast as it can be. Besides, for 10-100 objects, no matter what you do, it will probably appear instantaneous to the user.

Related

Merging multiple LiveData sources?

To make it easier to visualize my problem I drew the following:
I am using a RoomDatabase, a Repository, a Viewmodel and Livedata. Areas have a 1 to n relationship with Gateways and Gateways a 1 to n relationship with Items.
I created both an AreaWithGateways entity and a GatewayWithItems entity.
Items can move from a gateway to another, which is why I observe them with Livedata to keep track of which Gateway they're in. My problem now is that I found myself in need to also keep track of which Items are in which Areas and I can't figure out how to do that.
I've thought of merging the LiveData of each Gateway together and observing that using MediatorLiveData but I didn't really understand how to use it.
Or maybe it's possible to create an Entity AreaWithGatewayswithItems?
Any insight would be appreciated
Edit: I am adding some code to make the problem a bit clearer
This is the Area Entity
#Entity(tableName = "area_table")
public class Area {
#PrimaryKey(autoGenerate = false)
private int areaId;
private String areaName;
private float wertX;
private float wertY;
private Boolean isDrawn;
public int getAreaId() {
return areaId;
}
public void setAreaId(int areaId) {
this.areaId = areaId;
}
public String getAreaName() {
return areaName;
}
public float getWertX() {
return wertX;
}
public float getWertY() {
return wertY;
}
public Boolean getDrawn() {
return isDrawn;
}
public Area(int areaId, String areaName, float wertX, float wertY, Boolean isDrawn) {
this.areaId = areaId;
this.areaName = areaName;
this.wertX = wertX;
this.wertY = wertY;
this.isDrawn = isDrawn;
}
}
The Gateway Entity:
#Entity(tableName = "gateway_table")
public class Gateway {
private float temp;
private String title;
private int areaId;
#PrimaryKey(autoGenerate = false)
private int gatewayId;
public int getGatewayId() {
return gatewayId;
}
public void setGatewayId(int gatewayId) {
this.gatewayId = gatewayId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public float getTemp() {
return temp;
}
public void setTemp(float temp) {
this.temp = temp;
}
public int getAreaId() {
return areaId;
}
public Gateway(int areaId, int gatewayId, String title) {
this.title = title;
this.areaId = areaId;
this.gatewayId = gatewayId;
}
}
And the "Item" Entity:
#Entity(tableName = "cow_table")
public class Cow {
private int age;
private String sex;
private String name;
private boolean drawn;
private int gatewayId;
private String raceId;
#PrimaryKey(autoGenerate = false)
private int cowId;
public int getCowId() {
return cowId;
}
public void setCowId(int cowId) {
this.cowId = cowId;
}
public String getName() {
return name;
}
public boolean isDrawn() {
return drawn;
}
public int getAge() {
return age;
}
public String getSex() {
return sex;
}
public void setDrawn(boolean drawn) {
this.drawn = drawn;
}
public int getGatewayId() {
return gatewayId;
}
public String getRaceId() {
return raceId;
}
public Cow(int age, int cowId, int gatewayId,String name, String raceId, String sex, boolean drawn) {
this.age = age;
this.cowId = cowId;
this.sex= sex;
this.name = name;
this.drawn = drawn;
this.gatewayId = gatewayId;
this.raceId = raceId;
}
}
And this is the relation "AreaWithGateways":
public class AreaWithGateways {
#Embedded
private Area area;
#Relation(parentColumn = "areaId",
entityColumn = "areaId")
private List<Gateway> gatewayList;
public Area getArea() {
return area;
}
public List<Gateway> getGatewayList() {
return gatewayList;
}
public AreaWithGateways(Area area, List<Gateway> gatewayList) {
this.area = area;
this.gatewayList = gatewayList;
}
}
As well as GatewaysWithCows:
public class GatewayWithCows {
#Embedded
private Gateway gateway;
#Relation(parentColumn = "gatewayId",
entityColumn = "gatewayId")
private List<Cow> cowList;
public Gateway getGateway() {
return gateway;
}
public List<Cow> getCowList() {
return cowList;
}
public GatewayWithCows(Gateway gateway, List<Cow> cowList) {
this.gateway = gateway;
this.cowList = cowList;
}
}
I've been trying to find a way of getting all of the "items" in an Area as Livedata but still can't figure it out.
I feel like I should somehow use AreaWithGateways to add the LiveData items together but I can't reach the items through the gateways, it has to be the other way around.
Or maybe it's possible to create an Entity AreaWithGatewayswithItems?
Not an Entity as these are used to define tables BUT via POJO's using #Embedded and #Relation annotation (e.g. your GatewayWithCows is a POJO).
I feel like I should somehow use AreaWithGateways to add the LiveData items together but I can't reach the items through the gateways, it has to be the other way around.
You basically use a hierarchical approach but POJO's so as you have GatewayWithCows then relate to this from Area as per :-
class AreaWithGatewayWithCows {
#Embedded
Area area;
#Relation(entity = Gateway.class, parentColumn = "areaId",
entityColumn = "areaId")
List<GatewayWithCows> gatewayWithCowsList;
}
Note I missed the s after Gateway for the class name (and as such in the query below)
Note the use of entity = Gateway.class is required as the relationship is via the Gateway NOT via the GatewayWithCows (which isn't a table).
The Query Dao could be as simple as :-
#Query("SELECT * FROM area_table")
List<AreaWithGatewayWithCows> getAreaWithGatewaysWithCows();
amended accordingly for LiveData.
note that if you use JOINS in the query then the any clauses such as WHERE will only affect the Area's, not the underlying Gateways and Cows. That is irrespective of the query #Relation builds each Area with ALL Gateways related to that Area; and each Gateway gets ALL the Cows related to that Gateway.

How can I change the name of the Schema dynamically in JPA

I am using JPA to query flyway_schema_history to print a report of the executed migrations. The structure of flyway_schema_history is repeated across all the schemas in my Postgresql database. Also, I am passing the name of the schema as a parameter when I run the program.I am using Java 10.
I created this entity in JPA.
#Entity
#Table(name = "flyway_schema_history", schema="this_should_be dynamic")
public class FlywaySchemaHistoryGeneric {
#Id
#Column(name="installed_rank")
private Integer installedRank;
#Column(name="version")
private String version;
#Column(name="description")
private String description;
#Column(name="type")
private String type;
#Column(name="script")
private String script;
#Column(name="checksum")
private Integer checksum;
#Column(name="installed_by")
private String installedBy;
#Column(name="installed_on")
private Date installedOn;
#Column(name="execution_time")
private Integer executionTime;
#Column(name="success")
private Boolean success;
public Integer getInstalledRank() {
return installedRank;
}
public void setInstalledRank(Integer installedRank) {
this.installedRank = installedRank;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getScript() {
return script;
}
public void setScript(String script) {
this.script = script;
}
public Integer getChecksum() {
return checksum;
}
public void setChecksum(Integer checksum) {
this.checksum = checksum;
}
public String getInstalledBy() {
return installedBy;
}
public void setInstalledBy(String installedBy) {
this.installedBy = installedBy;
}
public Date getInstalledOn() {
return installedOn;
}
public void setInstalledOn(Date installedOn) {
this.installedOn = installedOn;
}
public Integer getExecutionTime() {
return executionTime;
}
public void setExecutionTime(Integer executionTime) {
this.executionTime = executionTime;
}
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
}
I was trying to use FlywaySchemaHistoryGeneric.class.getAnnotation to change the value in runtime without success, but I think that it should be an easy way to do it.
How can I do to make schema from #table to be dynamic?
I guess you wont be able to modidy that in runtime because annotations are read-only. You can create one datasource for each schema and choose one of them to persist the entity based in your rules.

Correct way to update a Realm database?

This is the structure of my Realm database:
public class ARDatabase extends RealmObject
{
#PrimaryKey
private String uid;
private String namex;
private String desc;
private boolean isVideo;
private boolean isDeleted;
private String urlImg;
private String urlApp;
private int updates;
private boolean isDownloaded;
private String location;
public ARDatabase(){}
public String getUid()
{
return uid;
}
public void setUid(String uid)
{
this.uid = uid;
}
public String getNamex()
{
return namex;
}
public void setNamex(String namex)
{
this.namex = namex;
}
public String getDesc()
{
return desc;
}
public void setDesc(String desc)
{
this.desc = desc;
}
public boolean getIsVideo()
{
return isVideo;
}
public void setIsVideo(boolean isVideo)
{
this.isVideo = isVideo;
}
public boolean getIsDeleted()
{
return isDeleted;
}
public void setIsDeleted(boolean isDeleted)
{
this.isDeleted = isDeleted;
}
public String getUrlImg()
{
return urlImg;
}
public void setUrlImg(String urlImg)
{
this.urlImg = urlImg;
}
public String getUrlApp()
{
return urlApp;
}
public void setUrlApp(String urlApp)
{
this.urlApp = urlApp;
}
public int getUpdates()
{
return updates;
}
public void setUpdates(int updates)
{
this.updates = updates;
}
public boolean getIsDownloaded()
{
return isDownloaded;
}
public void setIsDownloaded(boolean isDownloaded)
{
this.isDownloaded = isDownloaded;
}
public String getLocation()
{
return location;
}
public void setLocation(String location)
{
this.location = location;
}
}
And I can successfully add objects to the database.
The problem comes when I need to update an object.
This is what I tried:
private void downloadUpdateDatabase(String uid,String location_address) throws RealmException
{
mRealm.beginTransaction();
ARDatabase db = new ARDatabase();
db.setUid(uid);
db.setIsDownloaded(true);
db.setLocation(location_address);
mRealm.copyToRealmOrUpdate(db);
mRealm.commitTransaction();
Log.e("TAG","DOWNLOAD UPDATE COMPLETED");
}
The problem here is when I invoke this method. The mentioned fields get updated, but the not mentioned fields in this method become null or zero.
Of course I can set values for all fields by invoking their setters, however from where I invoke this method, I can't get all the field values.
So, the Question is: How do I update my realm database in such a way that the existing fields don't become null ?
P.S.:
My Realm version is :0.84.1, compile 'io.realm:realm-android:0.84.1'
the field that are mentioned gets updated, however the fields that are not mentioned in this method becomes null or zero
Well, yes, all fields are their defaults at this point.
ARDatabase db = new ARDatabase();
Have you tried to query for the current record, then update the fields, then put that object back?
In other words, you have String uid, so something like
private void downloadUpdateDatabase(String uid,String location_address) throws RealmException
{
mRealm.beginTransaction();
ARDatabase db = mRealm.where(ARDatabase.class).equalTo("uid", uid).findFirst();
db.setIsDownloaded(true);
db.setLocation(location_address);
mRealm.copyToRealmOrUpdate(db);
mRealm.commitTransaction();
}
Or, probably better in async fashion.
private void downloadUpdateDatabase(final String uid, final String location_address) throws RealmException
{
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
ARDatabase db = realm.where(ARDatabase.class).equalTo("uid", uid).findFirst();
db.setIsDownloaded(true);
db.setLocation(location_address);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
// Transaction was a success.
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
// Transaction failed and was automatically canceled.
}
});
}
Instead of
mRealm.beginTransaction();
ARDatabase db = new ARDatabase();
db.setUid(uid);
db.setIsDownloaded(true);
db.setLocation(location_address);
mRealm.copyToRealmOrUpdate(db);
mRealm.commitTransaction();
Log.e("TAG","DOWNLOAD UPDATE COMPLETED");
There should be
mRealm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
ARDatabase db = realm.where(ARDatabase.class).equalTo("uid", uid).findFirst();
if(db == null) {
db = realm.createObject(ARDatabase.class, uid);
}
db.setIsDownloaded(true);
db.setLocation(location_address);
}
});
Log.e("TAG","DOWNLOAD UPDATE COMPLETED");

Database access Java

I've got the following question. I got a little application which saves payments, dates and persons inside a database. Now I got the following POJO class:
public class Payment implements Serializable {
private int id;
private double payment;
private Date datum;
private String usage;
private String category;
private int importance;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getPayment() {
return payment;
}
public void setPayment(double payment) {
this.payment = payment;
}
public Date getDatum() {
return datum;
}
public void setDatum(Date datum) {
this.datum = datum;
}
public String getUsage() {
return usage;
}
public void setUsage(String usage) {
this.usage = usage;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public int getImportance() {
return importance;
}
public void setImportance(int importance) {
this.importance = importance;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("ID: ");
sb.append(id);
sb.append("\nPAYMENT: ");
sb.append(payment);
sb.append("\nDATE: ");
sb.append(datum);
sb.append("\nUSAGE: ");
sb.append(usage);
sb.append("\nCATEGORY: ");
sb.append(category);
sb.append("\nIMPORTANCE: ");
sb.append(importance);
return sb.toString();
}
}
So, I got also a class for my dates and persons. The question I've got is the following: Should I create for every Table in my database(in Java the Payment.class , Date.class and Person.class) a own transaction/access class which supports an .saveOrUpdate(), .list() or .delete() function?So maybe I got than a PaymentRansaction.class or an PersonTransaction.class.
Thanks for every help :)
It depends.
Do you have one table with transactions, then one model should be sufficient.
Create methods to create the transactions for you depending on Payment or Person.
BUT
If you have more then 1 table go for multiple classess, each table it's own class.

Retrieve a child of a child in the Firebase Recycler View

Gettin the child "sonyTV" in the Firebase Recycler View.
the problem : this child is not a direct child of "Users"
Got the name and the date ,but not "sonyTV"
#Override
protected void onBindViewHolder(#NonNull UsersViewHolder usersViewHolder, int i, #NonNull ALL_USERS all_users) {
usersViewHolder.setName(all_users.getName());
usersViewHolder.setDate(all_users.getDate());
usersViewHolder.setUserSoldItems(all_users.getUserSoldItems());
setUserSoldItems method
public void setUserSoldItems(ALL_USERS.UserSoldItems userSoldItems) {
TextView SonyTvView = mView.findViewById(R.id.showTVsony);
SonyTvView.setText("Sony TV : "+userSoldItems);
}
ALL_USERS class
public class ALL_USERS {
private String name;
private long date;
private UserSoldItems userSoldItems;
public ALL_USERS() {}
public ALL_USERS(String name, long date, UserSoldItems userSoldItems) {
this.name = name;
this.date = date;
this.userSoldItems = userSoldItems;
}
public String getName() { return name; }
public long getDate() { return date; }
public UserSoldItems getUserSoldItems() { return userSoldItems; }
public class UserSoldItems {
private long sonyTV;
public UserSoldItems() {}
public UserSoldItems(long sonyTV) {
this.sonyTV = sonyTV;
}
public long getSonyTV() { return sonyTV; }
}
}
but it gives me null values, although you can check it in my database its not null
This is how i post to child("sonyTV")
users.child(user.getUid()).child("UserSoldItems").child("sonyTV").runTransaction(new Transaction.Handler() {
#NonNull
#Override
public Transaction.Result doTransaction(#NonNull MutableData mutableData) {
Long value = mutableData.getValue(Long.class);
if (value == null) {
mutableData.setValue(0);
}
else {
mutableData.setValue(value + 1);
}
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
}
});
According to your question:
Dear Alex i have one more thing i cant get the child sonyTV in the RecyclerView ,is it beacause its not a direct child of Users node ??!!!
Now seeing what you are trying to achieve, the simplest solution I can think of is to add an extra property of type UserSoldItems in your User class. So your User class should look like this:
public class ALL_USERS {
private String name;
private long date;
private UserSoldItems userSoldItems;
public ALL_USERS() {}
public ALL_USERS(String name, long date, UserSoldItems userSoldItems) {
this.name = name;
this.date = date;
this.userSoldItems = userSoldItems;
}
public String getName() { return name; }
public long getDate() { return date; }
public UserSoldItems getUserSoldItems { return userSoldItems; }
}
And the UserSoldItems should look like this:
public UserSoldItems {
private long sonyTV;
public UserSoldItems() {}
public UserSoldItems(int sonyTV) {
this.sonyTV = sonyTV;
}
public long getSonyTV() { return sonyTV; }
}
Or even simpler:
public UserSoldItems {
public long sonyTV;
}
And please note, the name of the node should be UserSoldItems and not User Sold Items in order to make it work. So it should not contains any spaces. So when adding data, please add it without any space. In the end, just clear the actual data from the database and add fresh one.
So we introduced a new UserSoldItems level in your JSON tree, so you we can ensure that your Java classes reflect that.

Categories

Resources