Query nested Realm objects encapsulated in RealmList into RealmResults - android

I have the following RealmObject:
public class City extends RealmObject {
private String cityId;
private RealmList<Street> streets;
public String getId() {
return cityId;
}
public void setCityId(String cityId) {
this.cityId = cityId;
}
public RealmList<Street> getStreets() {
return streets;
}
public void setStreets(RealmList<Street> streets) {
this.streets = streets;
}
}
Now having a cityId I need to query streets of particular city. How to do that? What I did try was:
Realm.getInstance(context).where(City.class).equalTo("cityId", someCityId, false)
.findFirst().getStreets().where().findAll()
But this leads into an Exception. I need to display streets in a ListView implementing filtering so I need streets to be RealmResults to use RealmBaseAdapter<Street>.

The proper way would be to have an open Realm instance either opened in your Activity in onCreate() and closed in onDestroy(), or in your custom application class.
Then you can use this realm instance to query the realm
City city = realm.where(City.class).equalTo("cityId", cityId).findFirst();
Then you can access the RealmList<T> like any other list
RealmList<Street> streets = city.getStreets();
Then you can use a recyclerview to get the view for a given index within your streets list.

Related

How to retrieve correct subtype from firebase android getValue()

Hi all I can't think of a better example to illustrate my point so do let me know If my example has some errors. But hopefully this example will get my point through.
class A {
String CATEGORY = "A";
public String getCATEGORY() {
return CATEGORY;
}
}
class B extends A {
String CATEGORY = "B";
#Override
public String getCATEGORY() {
return CATEGORY;
}
}
class C extends A {
String CATEGORY = "C";
#Override
public String getCATEGORY() {
return CATEGORY;
}
}
public class MyClass {
private List<A> array = Arrays.asList(new A(), new B(), new C());
public MyClass() {}
}
Now if I upload MyClass onto firebase using setValue for example, firebase will show me the properties of class A, B and C. However, when I read the data from firebase and call sth like getValue(MyClass.class) the List it returns me are all of type A and the subclasses are not preserved. Is there a workaround to allow firebase to preserve the class types uploaded?
If you use Firebase's default serializer, it simply writes all public properties and fields to the database. Say that you store a single instance of each class, it'd be:
-L1234567890: {
cATEGORY: "A"
},
-L1234567891: {
cATEGORY: "B"
},
-L1234567892: {
cATEGORY: "C"
},
There won't be enough knowledge in the database for the SDK to reinflate the correct sub-class. While you and I can see that the cATEGORY value matches the class name, the Firebase SDK has no such knowledge.
It won't be too hard to write your own custom deserializer for this data though, taking a DataSnapshot with the values above and reinflating the correct class and values.
You could also do a hybrid: detect the class type directly, and then tell Firebase what class to read:
String cat = snapshot.child("cATEGORY").getValue(String.class)
Class clazz = "C".equals(cat) ? C.class : "B".equals(cat) ? B.class : A.clas;
A object = snapshot.getValue(clazz);

#Query Update in Android Room Database doesn't update primary key field

I'm writing Android app with Room Database. My database contains GroupVc entity with such code:
#Entity
public class GroupVc {
#ColumnInfo(name = "language")
private String language;
#NonNull
#PrimaryKey
#ColumnInfo(name = "name_group")
private String nameGroup;
public GroupVc(String language, String nameGroup) {
this.language = language;
this.nameGroup = nameGroup;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
#NonNull
public String getNameGroup() {
return nameGroup;
}
#NonNull
public void setNameGroup(String nameGroup) {
this.nameGroup = nameGroup;
}
}
As you can see my entity class contains two columns where nameGroup is my PrimaryKey. In my application I want to let the user to see the full list of groups and change the name of Group DialogFragments by entering new Group's names. To implement such function I've created the next DAO #Query method:
#Dao
public interface GroupVcDao {
#Query("UPDATE groupvc SET name_group= :newName WHERE name_group= :currentName")
void updateNameOfGroup(String currentName, String newName);
}
In this QUERY I want to change name of GroupVc by getting the existing name of GroupVc from RecyclerView (currentName param) and applying new one from DialogFragment (newName param).
My problem is that this QUERY doesn't bring any effect and doesn't update the name. Although I don't get any errors or exceptions. So I need to know: does such QUERY correct? Is it possible to write Update queries where primary key is changeable value and condition at the same time?
Here is the link to my complete project on GitHub
https://github.com/LAHomieJob/VocaNote
I can't be sure, but my guess would be that your query is working, but since you're changing the primary key your database is left with an instance of the old object as well as the new one. Try checking to see if you have an object with both the old name_group and the new name_group. Also if you're looking to allow your users to change the group name, it may make sense to move your primary key to a UUID or some other key that doesn't change.

Realm Custom Sort throws UnsupportedOperationException

Using Realm for Android and I have a custom sort which throws a java.lang.UnsupportedOperationException: Replacing and element is not supported
So, I have a piece of data that I need to sort after I have retreived it from a Realm database, so it is sitting in a List collection like this:
Collections.sort(stores, new DistanceComparator(currentLocation));
The DistanceComparator looks like this:
public class DistanceComparator implements Comparator<Restaurant> {
private Location currentLocation;
public DistanceComparator(Location location) {
currentLocation = location;
}
public int compare(RealmObject c1, RealmObject c2) {
if ((LocationUtil.calculateSomeValue(c1.getLocation()))
== (LocationUtil.calculateSomeValue(c2.getLocation()))) {
return 0;
}
else if ((LocationUtil.calculateSomeValue(c1.getLocation()))
< (LocationUtil.calculateSomeValue(c2.getLocation()))) {
return -1;
}
else {
return 1;
}
}
}
However, when I execute the sort, I am getting a:
java.lang.UnsupportedOperationException: Replacing and element is not supported.
at io.realm.RealmResults$RealmResultsListIterator.set(RealmResults.java:868)
at io.realm.RealmResults$RealmResultsListIterator.set(RealmResults.java:799)
at java.util.Collections.sort(Collections.java:247)
I cannot perform the sort within Realm due to the transient nature of the data.
Can you not perform a basic sort against a collection that happen to have Realm data or do I need to add some sort of annotation in the class that is a RealmObject in order to perform this type of sort?
I did take a look at this question and that is my fallback if this will not work: How do you sort a RealmList or RealmResult by distance?
You can't perform custom sorting on RealmResult. You'll have to copy your results to an unmanaged List and work with it instead of the realm results.
List<ModelMyFlirtsItem> storesList = realm.copyFromRealm(stores);
Collections.sort(storesList, new DistanceComparator(currentLocation));
You can consider moving the value evaluated by LocationUtil to be part of the RealmObject itself, and do the sort by Realm.
public int compare(RealmObject c1, RealmObject c2) {
if ((LocationUtil.calculateSomeValue(c1.getLocation()))
== (LocationUtil.calculateSomeValue(c2.getLocation()))) {
return 0;
}
else if ((LocationUtil.calculateSomeValue(c1.getLocation()))
< (LocationUtil.calculateSomeValue(c2.getLocation()))) {
return -1;
}
else {
return 1;
}
}
could be
public class RealmObject extends RealmObject {
private Integer distance;
private Location location;
public void setLocation(Location location) {
if(location == null) {
this.distance = null;
} else {
this.distance = LocationUtil.calculateSomeValue(location);
}
this.location = location;
}
}
Then
RealmResults<RealmObject> results = realm.where(RealmObject.class).findAllSorted("distance", Sort.ASCENDING);
I have also faced this problem for using sorting in RealmResults. I resolved it just using the Realm's native .sort() function. For example -
RealmResult<Location> stores......;
.......
.......
stores.sort(new DistanceComparator(currentLocation));

Filtering data before populating view from FirebaseRecyclerAdapter

Taking reference from FireChat app I used FirebaseRecyclerAdapter in my app, it is working fine but I need to filter data which equals to specific user ID.
In populateView if I does that data is getting filtered but a blank recycler Item item is also getting created.
mFirebaseAdapter = new FirebaseRecyclerAdapter<Tags, TagsViewHolder>(
Tags.class,
R.layout.item_message,
TagsViewHolder.class,
mFirebaseDatabaseReference.child("tags")) {
#Override
protected void populateViewHolder(TagsViewHolder viewHolder, Tags tags, int position) {
mProgressBar.setVisibility(ProgressBar.INVISIBLE);
if((tags.getUserid()).equals(mFirebaseUser.getUid()))
viewHolder.tagTextView.setText(tags.getTagname());
}
};
Is there any way I can filter it before a view gets created?
my POJO class is:
public class Tags {
private String userid;
private String tagname;
private HashMap<String, Object> timestampCreated;
public Tags() {
}
public Tags(String userid, String tagname, HashMap<String, Object> timestampCreated) {
this.userid = userid;
this.tagname = tagname;
this.timestampCreated = timestampCreated;
}
public String getUserid() {
return userid;
}
public String getTagname() {
return tagname;
}
public HashMap<String, Object> getTimestampCreated() {
return timestampCreated;
}
}
Thank you!
Client-side filtering is an open feature request for FirebaseUI. See https://github.com/firebase/FirebaseUI-Android/issues/15.
Client-side filtering of data, means that the app first download data and then doesn't show it to the user. This is wasteful and most users of mobile apps will appreciate it if your app only downloads data that it shows to them.
The best ways to deal with displaying a subset of the data is to either model the data in a way so that you can directly access the subset of data that you want to display, or to use Firebase queries to limit the data that is retrieved.

Parse Nested Query Android

Ok been stuck on this for literally weeks, sorry for long question.
Using Parse, making a workout app for Android.
Database tables and columns that are relevant to my problem are:
Exercises
ID| name | description | Musclegroup
Workouts
ID| workoutName| userID
WorkoutExercises (basically a join table between the two)
ID| workoutID (pointer) | exerciseID (pointer)
So a user will be able to create a workout and add in the exercises they want.
So far I've already done:
User can go to category, browse the muscle group, view the exercises in that group
signup/login/logout, update profile
list the current workouts (not the exercises in them) -
I have just entered some exercises into a workout on the db as have gotten stuck on querying current exercises in that workout before worrying about inserting new ones.
The problem:
I'm trying to do a nested query to get the exercise name so once a user clicks Eg. Back Workout it should bring up a list Eg. Deadlift, Rows, Chin ups
So basically in SQL I want to:
Select name
from Exercises
where ID in (select exerciseID from workoutexercises where WorkoutID=xxxx)
Things i'm struggling on:
Making a nested query for a pointer, from what I have read on the net I need to use query.include("exerciseID"); which will tell Parse to include a full Exercise item that I can then use to query? Correct me if wrong? Code below - have I done it correctly?
I've learnt from and been using methods from: http://www.michaelevans.org/blog/2013/08/14/tutorial-building-an-android-to-do-list-app-using-parse/ where query data is put into a custom adapter that lists the data. It uses getters
and setters to save/retrieve String/int values, do I still use the getters for getting a string from within a pointer?
EG.
public String getName()
{
return getString("name");
}
As in once i'm "through" that pointer and in the Exercise table im assuming i'm still just getting the String name value as oppose to getting a ParseObject?
Now so far I have been able to get the custom adapter to put 2 horizontal bars across the screen that shows it knows i've put 3 items in workoutExercises but just not bringing up the text from Exercise name that I need from the nested query
Have a look at my screenshots to see what I mean.
Thank you very much for the help in advance.
Query so far:
public void getcurrentExercisesInWorkout() {
//set progress bar
setProgressBarIndeterminateVisibility(true);
ParseQuery<WorkoutExercises> query = ParseQuery.getQuery("WorkoutExercises");
query.include("exerciseId");
query.whereEqualTo("workoutId", mWorkoutId);
query.findInBackground(new FindCallback<WorkoutExercises>() {
#Override
public void done(List<WorkoutExercises> workoutExercises, ParseException error) {
if (workoutExercises != null) {
mWorkoutExercisesAdapter.clear();
mWorkoutExercisesAdapter.addAll(workoutExercises);
} else {
Log.d("error", error.getMessage());
}
}
});
//stop progress bar
setProgressBarIndeterminateVisibility(false);
}
Custom list Adapter:
//constructor - get current state of parsed object and list of objects retrieved from workout
public WorkoutExercisesAdapter(Context context, List<WorkoutExercises> objects) {
super(context, R.layout.row_item, objects);
this.mContext = context;
this.mWorkoutExercises = objects;
}
public View getView(int position, View convertView, ViewGroup parent)
{
//put each item into the listview
if(convertView == null)
{
LayoutInflater mLayoutInflater = LayoutInflater.from(mContext);
convertView = mLayoutInflater.inflate(R.layout.row_item,null);
}
WorkoutExercises workoutExercises = mWorkoutExercises.get(position);
TextView nameView = (TextView) convertView.findViewById(R.id.row_name);
//this just calls a String getter - which isn't working - need it to get the Exercise name from within the pointer
nameView.setText(workoutExercises.getWorkoutExercise());
return convertView;
}
WorkoutExercises(stores the getters)
#ParseClassName("WorkoutExercises")
public class WorkoutExercises extends ParseObject {
public String exName;
public WorkoutExercises()
{
}
public String getWorkoutExercise()
{
return getString("name");
}
}
Running Android Studio in Debug mode I can literally see the data I am trying to put into the text field (see screenshot - how can I grab that value? See screenshot below
NOW WORKING - THE RESULT!
First, alter the WorkoutExercises object:
#ParseClassName("WorkoutExercises")
public class WorkoutExercises extends ParseObject {
//public String exName;
public static final String workoutID = "workoutID"
public static final String exerciseID = "exerciseID"
public Exercises getWorkoutExercise() {
return (Exercises)getParseObject(exerciseID);
}
public WorkoutExercises()
{
// not sure if it is allowed to define your own constructor?
// just a note
}
// WorkoutExercises does not have a 'name' col
//public String getWorkoutExercise()
//{
// return getString("name");
//}
}
I assume that Exercises at least contains something like:
#ParseClassName("Exercises")
public class Exercises extends ParseObject {
public static final String name = "name"
public String getName()
{
return getString(name);
}
}
Now, with the query on WorkoutExercises including workoutID, the Exercises object will be populated in the fetched query. This means you can do the following to get the name of the exercises object:
// workoutExercises returned from a query including workoutID
Exercises exercise = workoutExercises.getWorkoutExercise();
String name = exercise.getName();
// if Exercises has getters for description and Musclegroup
String description = exercise.getDescription();
String musclegroup= exercise.getMusclegroup();
Hope this sheds some light on the problem

Categories

Resources