how would I do a mass update in Realm Android? - android

I am currently doing the following but I don't think it's the efficient way of doing it:
Realm defaultInstance = Realm.getDefaultInstance();
RealmResults<Stamp> stamps = defaultInstance.where(Stamp.class).equalTo("exerciseGuid", exerciseGuid).findAll();
if (stamps.size() > 0) {
defaultInstance.beginTransaction();
for (int i = 0; i < stamps.size(); i++) {
Stamp stamp = stamps.get(i);
stamp.setSynced(false);
stamp.setName(newName);
}
defaultInstance.commitTransaction();
}

Not really a Realm user, but it looks like batch updates aren't implemented yet in realm-java and your way of doing massive updates is for now the only supported way.

Related

150k words text file is 0.8mb where realm db size is 18mb

I am inserting 150000 objects in realm db. Object has only one property which is string.
At the same time I am creating a string builder with new line for each string
and finally writing it into a text file.
At the end text file size is 0.8mb. Where realm db size is 18mb. What is the cause for it. How to minimize realm db size. Can you please helm me. Here is the realm insertion code
private void insertWord() {
long time = System.currentTimeMillis();
StringBuilder builder=new StringBuilder();
RealmConf conf = RealmConf.getInstance(true);
int i = 0;
RealmUtils.startTransaction(conf);
while (i < 150000) {
i++;
String word = "Word:" + i;
EB eb = new EB(word);
builder.append(word+"\n");
RealmUtils.saveWord(eb, conf);
Log.i("word check" + i++, "seelog:" + word);
}
RealmUtils.commitTransaction(conf);
writeStringIntoFile(builder.toString(),0);
}
You could try the following, for science:
private void insertWord() {
long time = System.currentTimeMillis();
StringBuilder builder=new StringBuilder();
RealmConf conf = RealmConf.getInstance(true);
int i = 0;
int batchCount = 0;
while (i < 150000) {
if(batchCount == 0) {
RealmUtils.startTransaction(conf);
}
batchCount++
i++;
String word = "Word:" + i;
EB eb = new EB(word);
builder.append(word+"\n");
RealmUtils.saveWord(eb, conf);
Log.i("word check" + i++, "seelog:" + word);
if(batchCount == 3000) {
RealmUtils.commitTransaction(conf);
batchCount = 0;
}
}
if(batchCount != 0) {
RealmUtils.commitTransaction(conf);
}
writeStringIntoFile(builder.toString(),0);
}
Probably because you forgot to call Realm.close().
Refer to this document for more details.
https://realm.io/docs/java/latest/#faq
Large Realm file size You should expect a Realm database to take less
space on disk than an equivalent SQLite database, but in order to give
you a consistent view of your data, Realm operates on multiple
versions of a Realm. This can cause the Realm file to grow
disproportionately if the difference between the oldest and newest
version of data grows too big.
Realm will automatically remove the older versions of data if they are
not being used anymore, but the actual file size will not decrease.
The extra space will be reused by future writes.
If needed, the extra space can be removed by compacting the Realm
file. This can either be done manually or automatically when opening
the Realm for the first time.
If you are experiencing an unexpected file size growth, it is usally
happening for one of two reasons:
1) You open a Realm on a background thread and forget to close it
again.
This will cause Realm to retain a reference to the data on the
background thread and is the most common cause for Realm file size
issues. The solution is to make sure to correctly close your Realm
instance. Read more here and here. Realm will detect if you forgot to
close a Realm instance correctly and print a warning in Logcat.
Threads with loopers, like the UI thread, do not have this problem.
2) You read some data from a Realm and then block the thread on a
long-running operation while writing many times to the Realm on other
threads.
This will cause Realm to create many intermediate versions that needs
to be tracked. Avoiding this scenario is a bit more tricky, but can
usually be done by either either batching the writes or avoiding
having the Realm open while otherwise blocking the background thread.

Android Realm for-loop - RealmResults are removed during looping

I have some code running in an IntentService, doing image uploads. It starts by fetching posts that are "queued" for upload, then it loops through those and makes synchronous Retrofit calls to upload images. Fairly straight-forward. See code for reference:
final RealmResults<Post> posts = realm.where(Post.class)
.equalTo("uniqueCode", uniqueCode)
.equalTo("queued", true)
.isEmpty("url")
.findAll();
Log.d(TAG, "post count: " + posts.size());
if (posts != null && posts.size() > 0) {
for (int i = 0; i < posts.size(); i++) {
Log.d(TAG, "posts count now: " + posts.size());
Post post = posts.get(i);
Post submittedPost = api.uploadPhoto(<params>); // Retrofit call, which works fine
if (submittedPost != null) {
realm.beginTransaction();
post.setQueued(false);
post.setUrl(submittedPost.getUrl());
realm.commitTransaction();
sendBroadcastUpdate(); // This updates the UI in places
}
}
}
Oddly enough, each time it goes through the for-loop, the size of the results ("posts" in my case above) goes down by one - this is confirmed by my Log output that decrements by one each time, so it only gets through a few of the results. It's almost as if each time I'm committing the Realm transaction during the looping, it's updating my fetched query results, even though that array is set to be final.
I confirmed that it doesn't do this if I don't set those values ('queued' and 'url'). Which tells me it's updating the results somehow. I've tried different things, such as a while-loop (i.e. "while (posts.size() > 0)"), but it gets through 2-3 of them, and then all of a sudden the size of "posts" is immediately 0, for no reason at all that I can see.
I've also tried doing the begin/commit before and after the loop, but it yields similar results. Same goes for if I convert it to an array before processing. It seems that it always gets through a few of them, and then the size is automatically set to 0, so it exits the loop.
This strikes me as very bizarre, especially since I set the results to be "final" - is this expected behavior? Does anyone know a way around this, by chance?
For reference, we are using Realm version: 0.86.
In Realm versions where version < 0.89.0 or version >= 3.0.0*, this is expected behavior (see here).
RealmResults is a view to the latest version of the database (for a given object type where given conditions are met), and a transaction is essentially "creating the latest version of the database" meaning the RealmResults starts to see the new modified data with each modification.
See following:
final RealmResults<Post> posts = realm.where(Post.class)
.equalTo("uniqueCode", uniqueCode)
.equalTo("queued", true) // <---- condition true
.isEmpty("url")
.findAll();
...
if (submittedPost != null) {
realm.beginTransaction();
post.setQueued(false); // <---- condition false
post.setUrl(submittedPost.getUrl());
realm.commitTransaction();
sendBroadcastUpdate(); // This updates the UI in places
}
As you create a transaction, you start to see the latest version, in which case the RealmResults will no longer contain elements that have queued == false.
For Realm 0.88.3 or older, you need to iterate the RealmResults in reverse, or "iterate while the results isn't empty" (I used this method a lot before the 0.89.0 breaking change killed it, but it'd work with 3.0.0+ again so that's nice)
realm.refresh(); // enforce the next RealmResults to be *definitely* up-to-date
final RealmResults<Post> posts = realm.where(Post.class)
.equalTo("uniqueCode", uniqueCode)
.equalTo("queued", true)
.isEmpty("url")
.findAll();
while(!posts.isEmpty()) {
Post post = posts.get(0);
Post submittedPost = api.uploadPhoto(<params>); // Retrofit call, which works fine
if (submittedPost != null) {
realm.beginTransaction();
post.setQueued(false);
post.setUrl(submittedPost.getUrl());
realm.commitTransaction();
sendBroadcastUpdate(); // This updates the UI in places
}
}
For Realm 3.0.0+, you can either use for(Post post : results) { (iterators), or you can use a collection snapshot directly.
final RealmResults<Post> results = realm.where(Post.class)
.equalTo("uniqueCode", uniqueCode)
.equalTo("queued", true) //
.isEmpty("url")
.findAll();
final OrderedRealmCollection<Post> posts = results.createSnapshot(); // <-- snapshot
for (int i = 0; i < posts.size(); i++) {
//...
*(and honestly, the behavior in-between was a hack, where the RealmResults was not synchronized to see the latest version, and 3.0.0 had to undo this hack)
This is a normal behavior of an Iterator. Once you call .next(), you get the item and it is removed from the iterator. If you want to keep items, write a utility to convert an iterator to an ArrayList or some similar thing. You can read more about iterators here: https://www.tutorialspoint.com/java/java_using_iterator.htm

Fetch a single column from Realm Database (Android)

I'm a beginner in Realm.
I have a table with 3 columns which named Id, Name, Email,Address.
To get the data of Name column, we use a query like 'SELECT Name from table_name' for SQLite.
If we using Realm in Android, then which method do we have to use for fetching the data of only one column?
I searched alot on Google & documentation but to no avail.
Could anyone help me?
Update:
What I am tried:
RealmResults<User> results = query.findAll();
ArrayList<String> name = new Arraylist();
for(i=0; i<results.size; i++){
name.add(result.get(i).getName();
}
My problem:
results.size() > 10k. So I want to avoid 10k iteration
for(i=0; i<results.size; i++){
}
Look at queries section at the documentation:
All fetches (including queries) are lazy in Realm, and the data is never copied.
This mean, that data of particular column (property) will be fetched when you call getMyProperty() method. Not after call of finadAll() method of RealmQuery object
If we using Realm in Android, then which method do we have to use for fetching the data of only one column?
You can't, because Realm is an object store, it doesn't have concept of "columns".
My problem:
results.size() > 10k. So I want to avoid 10k iteration
for(i = 0; i < results.size(); i++){
}
Solution: don't iterate?
RealmResults<User> results = query.findAll();
//List<String> name = new ArrayList<>();
//for(i = 0; i < results.size(); i++){
// name.add(result.get(i).getName();
//}
return results;
// ...
String name = results.get(position).getName();

How to add 1 milion items in Realm correctly?

I want to choose between native SQLiteDatabase and Realm to deal with a big amount of data.
For benchmark I add to storage 1 milion of Product entities:
{id:integer,sku:string,name:string,data_creating:string}
Using SQLiteDatabase it takes near 1 minute 34 seconds on my device.
Using Realm it takes more them 10 minutes.
My code is:
Realm realm = Realm.getInstance(getApplicationContext());
realm.beginTransaction();
for(int i = 0 ; i < 1000000;i++){
Product product = realm.createObject(Product.class);
product.setId(i+1);
product.setName("Product_"+i);
product.setSku("SKU__"+i);
product.setDateCreated(new Date());
}
realm.commitTransaction();
How can I improve my code for better time performance?
The original question spawned a discussion within Realm, and we ended up adding a faster method to insert objects. The code for creating and inserting 1 mio objects can now be written as:
final Product product = new Product();
final Date date = new Date();
try(Realm realm = Realm.getDefaultInstance()) {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
for(int i = 0 ; i < 1000000; i++){
product.setId(i+1);
product.setName("Product_"+i);
product.setSku("SKU__"+i);
product.setDateCreated(date);
realm.insert(product);
}
}
});
}
You have to be aware that SQLite and Realm are two very different things. Realm is an object store and you are creating a lot of objects in the code shown above. Depending on your model class and the number of rows/objects, you will often see that Realm is a bit slower on inserts. To do a fair comparison, you could compare Realm with one of the many excellent ORMs out there.
Said that, Realm offers a low-level interface (io.realm.internal). I wouldn't recommend you to use it as it is currently undocumented. Your example would look like this:
long numberOfObjects = 1000000;
SharedGroup sharedGroup = new SharedGroup("default.realm");
WriteTransaction writeTransaction = sharedGroup.beginWrite();
Table table = writeTransaction.getTable("class_Product");
table.addEmptyRows(numberOfObjects);
for (int i = 0; i < numberOfObjects; i++) {
table.setLong(0, i, i); // id
table.setString(1, i, "Product_"+i); // name
table.setString(2, i, "SKU__"+i); // sku
table.SetDate(3, i, new Date()); // date
}
writeTransaction.commit();
sharedGroup.close();
You can now compare two table/row oriented data stores, and you will probably find that Realm is a bit faster than SQLite.
At Realm, we have a few ideas on how to get our object interface to run faster, and we hope to be able to implement them in the near future.

For-loop not working the way i want it to

I have a extremely minor issue that I can't seem to figure out. I'm trying to extract data based on a type of value from an ArrayList> and place it into another ArrayList. The issue is that the for-loop only runs once, which in this case i need it to traverse the entire array and then place the data into the unSuppressedData arraylist.
Below is the for-loop:
for (int x = 0; x < suppressedStatus.length; x++) {
for (int i = 0; i < availData.size(); i++) {
Hashtable<String,String> checkAvail = availData.get(i);
String itemStatus = checkAvail.get("loanStatus");
if (unSuppressedData.contains(checkAvail) == false) {
if (!(itemStatus.equals(suppressedStatus[x]))) {
Log.d("Item Status", itemStatus);
Log.d("Suppressed Status", suppressedStatus[x]);
unSuppressedData.add(checkAvail);
//break;
}
}
}
}
suppressedStatus is a String array
availData is the arraylist i want to extract data from
unSuppressedData is the arraylist i want to place the data in
I believe that it only runs once is due to this line of code:
if (unSuppressedData.contains(checkAvail) == false) {
But i need to this line to check whether my unSuppressdData has the data, if no then will add the data from availData arraylist into unSuppressedData arraylist.
Could it be that i'm writing this piece of code wrongly? Appreciate any insights shed on this.
A good collection type for this sort of thing is the LinkedHashSet. Because it's a set, each element can only be added once. Being a hash, the contains test is quick. Being 'linked' the resulting set is iterated in insertion order.

Categories

Resources