Firebase toObject with ref - android

With cloud firestore, you can convert a document to your object with document.toObject(YourClass.class); where the class's variables match those in your database. However, if one of the variables in the database is a reference, which data type would you use in the java class? Note that I need to not only store it in my data model but retrieve it and set it with the override methods of the form:
protected MyDataModel(Parcel in) {
mString = in.readString();
}
and
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(mString);
}
If I use a DocumentReference, then in writeToParcel(), how would I write it? Also, how would I read it in the protected Parcel in constructor? I have tried writeSerializable() and readSerializable() but DocumentReference does not implement Serializable.

I haven't used the Java API yet but I imagine you'll want to use the DocumentReference type. Here are some links in the firebase documentation:
DocumentReference
CollectionReference
getDocumentReference

If you need to serialize a DocumentReference, use its getPath() method to get a string that describes the document's location in the database. To deserialize that path string back into a DocumentReference, use FirebaseFirestore.getInstance().document(path).
When you have conversions like this to make, sometimes it more convenient to write all the code yourself rather than using the built-in object serialization provided by the Firestore SDK.

I had the same problem with document.toObject(YourClass.class);
I have an Event class, this class contains the id (document id) and a reference to another collection subCategoryReference.
I have created simples converters on my Event
For example:
val event = document
.toObject(Event::class.java)
.withId<Event>(document.id)
.withReference<Event>((document.data["subCategoryReference"] as DocumentReference).path)
In my Event class as you guessed I have two function withId and withReference
that look like this:
fun <T : Event?> withId(id: String): T {
this.id = id
return this as T
}
fun <T : Event?> withReference(reference: String): T {
this.subCategoryRef = reference
return this as T
}
Your class variables must have different names from your firestore variables/fields (just the variables you want to apply the converter on) - see the pic below
In my case on firestore, the reference field (variable) called subCategoryReference and in the Event class the variable name is subCategoryRef.
Let me know if you didn't get me.
I hope it helps...
Firestore console
Event Class

Related

Can i create object to object map inside Google firestore console?

I have following object:
public class Cart {
public String id;
public List<Map<Product, Double>> productsInCart;
}
In this key itself is a complex object, how to do it in firestore?
Also above object can be redesigned as following
public class Cart {
public String id;
public Map<Product, Double> productsInCart;
}
Firestore does not have any sense of "complex" keys or properties. Fields and nested object property names must be strings. If you need to store something more complex, you will need to somehow reduce it down to a unique string. One option is to use a hash of the data in the object, but you're better off redesigning your object to simply use strings as keys.

Remove duplication from an array of objects

I have an object like below,
class LocationData{
String time;
String name;
String address;
}
for this object i have created getter setter.
By using service i fill this above model and save into room database.
whenever user open my app i just update the room database data to server using API.
Now sometimes the time duplication occurred. How to remove the object from array based on time. time should be unique.
You can use the extension function distinctBy. If you have an array of LocationData objects called allLocations it would be
val distinctLocations = allLocations.distinctBy { it.time }
Note distinctLocations will be a List; if you want it to be an array, use toTypedArray()

Practical use of #Ignore in Realm?

I've been trying to add Realm in my Android app. Their docs are pretty well explained & easy to follow. But it fails to explain this one particular area. I'm unable to figure out the practical use for the #Ignore annotation. I know that fields under this annotation are not persisted.
Can someone please share a few use cases. Also I wanted to know the scope of such fields. I mean, if I set an #Ignore field to some value, would that value be available to the other classes in my app for that particular launch session. If yes, then how do we access it? If no (which I guess is the case), then why do we need such a field anyway?
I've searched here and on web but couldn't find the relevant information. If out of my ignorance, I've missed upon some resource, please guide me to it.
Thanks.
Accordingly to the official documentation (see https://realm.io/docs/java/latest/) #Ignore is useful in two cases:
When you use GSON integration and your JSON contains more data than you want to store, but you still would like to parse it, and use right after.
You can't create custom getters and setter in classes extending RealmObject, since they are going to be overridden. But in case you want to have some custom logic anyway, ignored fields can be used as a hack to do that, because Realm doesn't override their getter & setters. Example:
package io.realm.entities;
import io.realm.RealmObject;
import io.realm.annotations.Ignore;
public class StringOnly extends RealmObject {
private String name;
#Ignore
private String kingName;
// custom setter
public void setKingName(String kingName) { setName("King " + kingName); }
// custom getter
public String getKingName() { return getName(); }
// setter and getter for 'name'
}
Ignored fields are accessible only from the object they were set in (same as with regular objects in Java).
UPDATE: As the #The-null-Pointer- pointed out in the comments the second point is out of date. Realm now allows having custom getters and setters in Realm models.
Here's a couple of real-world use cases:
1 - Get user's fullname:
public class User extends RealmObject {
private String first;
private String last;
#Ignore
private String fullName;
public String getFullName() {
return getFirst() + " " + getLast();
}
Get JSON representation of object:
public class User extends RealmObject {
private String first;
private String last;
#Ignore
private JSONObject Json;
public JSONObject getJson() {
try {
JSONObject dict = new JSONObject();
dict.put("first", getFirst());
dict.put("last", getLast());
return dict;
} catch (JSONException e) {
// log the exception
}
return null;
}
I've found it useful to define field names for when I am querying. For example
User.java
public class User extends RealmObject {
#Index
public String name;
#Ignore
public static final String NAME = "name";
}
And then later on I can do something like:
realm.where(User.class).equalTo(User.NAME, "John").findFirst();
This way if the schema changes from say name to id I don't have to hunt down every occurrence of "name".
Please see the the official documentation about #Ignore annotation:
The annotation #Ignore implies that a field should not be persisted to disk. Ignored fields are useful if your input contains more fields than your model, and you don’t wish to have many special cases for handling these unused data fields.

Set value to a realm object

I have following class
public class Student extends RealmObject{
private int studentID;
private String studentName;
// getters and setters here
}
Then I try to set a value to a already created student object
student.setStudentName("Peter");
Then I get following error
java.lang.IllegalStateException: Mutable method call during read
transaction.
In order to overcome this I have to do it as follows
Realm realm = Realm.getInstance(this);
realm.beginTransaction();
student.setStudentName("Peter");
realm.commitTransaction();
I don't want to persist this change in the database. How can I just set/change a value to an realm object variable without always persisting it to the database?
If you want to modify the object in a non-persisted manner, you need an unmanaged copy of it.
You can create a copy using realm.copyFromRealm(RealmObject realmObject); method.
When you are using Realm.createObject(), the object is added to the Realm and it only works within a write transaction. You can cancel a transaction and thereby discard the object.
Moreover, you can use your model class as a standalone class and create objects in memory (see http://realm.io/docs/java/0.80.0/#creating-objects for details). If you need to persist the objects, you can use the Realm.copyToRealm() method.
You might want to create a new Model. And your new model should implement RealmModel.
public class StudentRM extends RealmModel{
private int studentID;
private String studentName;
// Constructors here
// getters and setters here
}
Now you can do this.
studentRm.setStudentName("Peter"); //Setting Vale Or
studentRm.addAll(student); //Add all value from DB
studentRm.setStudentName("Jhon"); //It won't change DB anymore
studentRm.getStudentName(); // "Jhon"
You can use realm.cancelTransaction();, instead of realm.commitTransaction();

how to link my getEmptyForeignCollection() object with my parent object?

I want to persist an object with two foreignCollections.
But when I try to query the object, my foreignId is always null.
I already read this answers but it doesn't really help me: Collections in ORMLite
VOPerception perception = new VOPerception();
perception.setOrientation(daoOrientation.createIfNotExists(
orientationLocalizer.getCurrentOrientation()));
ForeignCollection<VOAccessPoint> fAp =
daoPerception.getEmptyForeignCollection("accessPoints");
fAp.addAll(wifiLocalizer.getCurrentScanResultMap());
perception.setAccessPoints(fAp);
daoPerception.create(perception);
List<VOPerception> list = daoPerception.queryForAll();
here data are correctly stored but VOAccessPoint objects have no link with the parent VOPerception object.
Here are my two classes:
public class VOPerception {
#DatabaseField(generatedId=true)
private int per_id;
#ForeignCollectionField(eager=true)
ForeignCollection<VOAccessPoint> accessPoints;
...
}
public class VOAccessPoint{
#DatabaseField(generatedId=true)
private int ap_id;
#DatabaseField(foreign=true,columnName="apForeignPerception_id")
private VOPerception apForeignPerception;
...
}
Your queryForAll() is returning no objects because none of your VOAccessPoint instances ever set their apForeignPerception field to be perception. Adding the VOAccessPoint objects using the ForeignCollection added them to the DAO but did not automagically assign their apForeignPerception field.
You should do something like:
...
Collection<VOAccessPoint> points = wifiLocalizer.getCurrentScanResultMap();
for (VOAccessPoint point : points) {
point.setApForeignPerception(perception);
}
fAp.addAll(points);
...
I can see how you might think that this would be handled automagically but at the time they are added to the ForeignCollection, the perception is not even assigned. I suspect that there is a missing feature for ORMLite here or at least a better exception.
I would recommend to use assignEmptyForeignCollection(Obj parent, fieldName). This will create a new foreign collection and all objects you will add via add(Obj element) will have the parent value set automatically.

Categories

Resources