Understanding Queries in Realm Java using Android Studio (PART 5)

in #utopian-io6 years ago

realm logo

Repository

https://github.com/realm/realm-java

What Will I Learn?

  1. You will learn about Queries in Realm
  • distinct() predicate
  • how to chain queries in Realm

Requirements

  • An Integrated Development Environment(IDE) for building Android Application(e.g Android Studio, IntelliJ)
  • Android Device/Virtual Device.
  • Little Experience in working with Realm Java.
  • Java Programming Experience.
  • Of course, willingness to learn

Resources

Difficulty

  • Intermediate

Tutorial Duration - 30 - 35Mins

Tutorial Content

In Today's tutorial, we are going to continue learning about queries in Realm. Previous tutorial links can be found below :

Recap of Part 1

In part 1 of this tutorial, we looked at three predicates - contains(),beginsWith(),in() and we were able to understand this by creating an android application that demonstrates the uses of this predicates.

We learnt that predicates that the form of `predicateName("field","query string",OPTIONAL ARGUMENT);

e.g - contains("name","Edet Ebenezer",Case.SENSITIVE);

The above code checks if any object's name field contains the text - Edet Ebenezer without paying attention to the cases.

We also learned how to use the third optional argument for the predicates which are Case.INSENSITIVE and Case.SENSITIVE where the former ignores the case of the query string to the contents of the field specified and the latter doesn't ignore it.

Recap of Part 2

In the part two of this tutorial series, we looked at the - beginsWith(), endsWith(),like() predicates.

The beginsWith() predicate checks to ensure that an object's field begins with the query string passed as the second argument to it and then it also takes the optional third case argument - Case.SENSITIVE or Case.INSENSITIVE, while the endsWith() predicate does the opposite of the beginsWith() predicate where it checks to ensure that an object's field ends with the query string passed as the second argument to it, and it also takes the third optional case argument.

Meanwhile, the like() predicate performs a glob style wildcard matching with the following :

  • ? matches a single unicode character
  • * matches 0 or more unicode characters.

Recap of Part 3

In the part three of this series, we looked at the between(),greaterThan(),lessThan(),greaterThanOrEqualTo(),lessThanOrEqualTo() predicates.

We learned the following about the above predicates :

  1. between() predicates takes three arguments, first the name of the object's field - e.g "age", secondly a lower bound and then lastly an upper bound e.g between("age", 5, 9) - this gives returns objects that their age field is between 5 and 9.
  2. greaterThan() predicates takes two arguments, first the field name and then the query integer e.g - greaterthan("age", 56) which returns objects that their age field's value is greater than 56
  3. lessThan() predicates takes the same arguments as the greaterThan() predicates e.g - lessThan("age", 20) which returns object that their age field value is less than 20.
  4. greaterThanOrEqualTo() predicates takes the same number of argument as specified above e.g greaterThanOrEqualTo("age", 30) which return objects that their age field has a value greater than or equal to 30.
  5. lessThanOrEqualTo() predicate takes the same number of argument as specified above e.g lessThanOrEqualTo("age", 75) which returns the objects that their age field value is less than or equal to 75.

Recap of Part 4

In part 4 of this tutorial, we looked at three predicates namely :

  1. beginGroup() and endGroup() predicate starts and ends a group of predicates and also specifies the order of evaluation - e.g
RealmResults<Person> result = realm.where(Person.class)
            .greaterThan("age", 15)
            .beginGroup()
                .contains("email","gmail.com")
                .and()
                .contains("address","Nigeria")
            .endGroup()
            .findAll();

The above query finds Person objects that are older than 15 and their email contains the words gmail.com AND their address contains the words Nigeria.
2.not() predicate negates a predicate - e.g

RealmResults<Person> result = realm.where(Person.class)
            .greaterThan("age", 15)
            .not()
            .beginGroup()
                .contains("email","gmail.com")
                .and()
                .contains("address","Nigeria")
            .endGroup()
            .findAll();

The above code does the opposite of the one before it meaning it queries for Person objects that are older than 15, but their emails don't contain the word - gmail.com and their address dont contain the word - Nigeria
3.sort() predicate is used to sort results of queries and it takes two arguments meanwhile it sorts by ASCENDING order by default -
- The field on which the sorting will be done.
- An optional sort parameter - Sort.ASCENDING or Sort.DESCENDING.
e.g result = result.sort("age",Sort.ASCENDING) or result = result.sort("age",Sort.DESCENDING)

Todays Tutorials

In today's tutorial, we are going to be learning about more query predicates in Realm, we are going to be learning about the distinct() predicate and also how to chain queries.

In order to achieve this, we are going to modify the existing android application that we have developed in tutorials 1 - 4.

Outline

  • Dependencies Used
  • Show predicates illustrations

Dependencies used

Please refer to the first tutorial for the dependencies used in this tutorial as no new dependencies have been added

Show predicates illustrations

In order to show illustrate the usage of today's predicates, the following modifications will be made:

  1. DoubleNumericQuery Fragment
    • We would modify our between() predicate code block to illustrate the distinct() predicate
  2. NumericQuery Fragment
    • We would be modifying our lessThan() predicate code block to illustrate query chaining

DoubleNumericQuery
Our DoubleNumericQuery Fragment formally accepted two inputs from the user - first and second age and then we used the between() predicate to find Person objects that their age field value falls between this range (first age - second age).

We will then be using the distinct() predicate together with the between() predicate.

The distinct() predicate returns only unique values based on the field specified, meaning if we have a realm database of four objects having two objects with the same age field value and we specify the age field in our distinct() predicate, the RealmResult list will, therefore, return 3 objects rather than four.

To illustrate this, we will be modifying our persons.json file to be this -
NB : Below code will only contain alterations, other objects will be specified after comments - //

[
  {
    "name": "Ebenezer Edet",
    "address": "Garki Abuja",
    "phone_number": 242424,
    "email": "[email protected]",
    "age": 30
  },
  //Six other json objects
  {
    "name":"Ebenezer Edet",
    "address":"Lugbe Abuja",
    "phone_number":345678,
    "email":"[email protected]",
    "age":45
  },
  //Two other json objects
]

In our DoubleNumericQuery java class file, inside our onClick() method for our GO button, we edit the codes as below -

@OnClick(R.id.go_btn)
public void onViewClicked() {
String age = queryName.getText().toString();
String age2 = queryName2.getText().toString();

StringBuilder toDisplay = new StringBuilder();

if ((!(age.isEmpty() && age2.isEmpty()))&& (Integer.valueOf(age2) > Integer.valueOf(age))) {

    RealmResults<Person> result = realm.where(Person.class)
            .between("age", Integer.valueOf(age),Integer.valueOf(age2))
            .distinct("name")
            .findAll();

    toDisplay.append("between() Predicate\n\n");
    toDisplay.append("There are - " + result.size() + " Persons between " + age + " and "+age2+"\n\n");

    result = result.sort("age", Sort.ASCENDING);
    toDisplay.append("ASCENDING ORDER BY AGE\n\n");
    int i = 0;

    while (i < result.size()) {
        toDisplay.append(result.get(i).name + " with phone number : " + result.get(i).phone_number + " email : " + result.get(i).email + " Address :" + result.get(i).address + " and age : "+ result.get(i).age +"\n\n\n");
        i++;
    }
    resultTv.setText(toDisplay.toString());
}
else
    showToast("No Field can be Empty and First Age must be greater than Second Age");

Code Explanation

  1. We get the value of the two EditText's and store in string variables - age and age2.
  2. We declare a StringBuilder object - toDisplay
  3. We use an if statement to ensure that the age and age2 variables are not empty and also that the age2 variable is greater than the age variable else we simply call the showToast() method with the argument -"No Field can be Empty and First Age must be greater than Second Age".
  4. We create a RealmResult object - result and then we use the between() predicate with the where statement specifying the Person class and then setting the lower and upper bound values of the between() predicate to be age and age2.
    • We then add the distinct() predicate specifying the name field as the argument which means that we only want unique names to be returned and duplicate names ignored.
  5. We append the text - between() predicate and also append the size of the objects that are returned by the query by - result.size() and append the age and age2 values.
  6. We then set the value of result to an ASCENDING sorted value of the result gotten by - result = result.sort("age"); where age is the field we sorted by.
  7. We append the text - ASCENDING ORDER BY AGE
  8. We then use a while loop to loop through the objects that match our query and then display the details of each object.
    • name : result.get(i).name
    • phone number : result.get(i).phone_number
    • email : result.get(i).email
    • address : result.get(i).address
    • age : result.get(i).age
  9. We then repeat the same steps inorder to sort the result object in a DESENDING order still using the age field.
    • here - result = result.sort("age",io.realm.Sort.DESCENDING); we initialze the result object to itself but sorted in DESENDING order
  10. We then display our result in our TextView - resultTv.setText(toDisplay.toString());

NumericQuery

In the NumericQuery fragment class, we are going to be editting our lessThan() predicate code section and demostrate query chaining which allows us to run additional queries on result sets.

For instance, if you want to get all objects that are younger than 50 and then you want to get a specific Person in that result set - e.g a 30 old person or one that his address contains - Nigeria, you will then need to chain the queries as explained below.

NB: Query chains are built on RealmResults objects and not RealmQuery object.

In the onClick() method of the GO button , edit to contain the following code

@OnClick(R.id.go_btn)
public void onViewClicked() {

String age = queryAge.getText().toString();
String specificAge = queryEmail.getText().toString();

StringBuilder toDisplay = new StringBuilder();

if (!(age.isEmpty() && specificAge.isEmpty())){

            RealmResults<Person> result = realm.where(Person.class)
                    .lessThan("age",Integer.valueOf(age))
                    .findAll();

            toDisplay.append("lessThan() Predicate\n\n");
            toDisplay.append("There are - " + result.size() + " Persons younger than " + age + "\n\n");

            toDisplay.append("These are the objects matching the lessThan() predicate");

            int i = 0;
            while (i < result.size()) {
                toDisplay.append(result.get(i).name + " with phone number : " + result.get(i).phone_number + " email : " + result.get(i).email + " Address :" + result.get(i).address + " and age : "+ result.get(i).age +"\n\n\n");
                i++;
            }

            toDisplay.append("Query Chain Result\n\n");
            Person singlePerson = result.where().equalTo("age",Integer.valueOf(specificAge)).findFirst();
            if(!(singlePerson == null)){
                toDisplay.append("This is the object returned after query chaining");
                toDisplay.append(singlePerson.name + " with phone number : " + singlePerson.phone_number + " email : " + singlePerson.email + " Address :" + singlePerson.address + " and age : "+ singlePerson.age +"\n\n\n");
            }
            else
                toDisplay.append("No Person in our result set has an age of "+specificAge);
    resultTv.setText(toDisplay.toString());
}
else
    showToast("No Field can't be empty");

Code Explanation

  1. We get the value of the two EditText's and store in string variable - age and specificAge.
  2. We declare a StringBuilder object - toDisplay
  3. We use an if statement to ensure that the age and specificAge variables are not empty else we simply call the showToast() method with the argument -"No Field can't be empty"
  4. We then create a RealmResult variable - result and the Person class is specified joined with the lessThan() predicate and the age entered by the user is the second query.
  5. We then append the text - "lessThan() Predicate" and then on the next line we append the size of the object return using - result.size().
  6. Next, we display the details of all the Person objects that meet our query using a while loop and the details are gotten by :
    • name : result.get(i).name
    • phone number : result.get(i).phone_number
    • email : result.get(i).email
    • address : result.get(i).address
    • age : result.get(i).age
  7. Next, we append the text - "Query Chain Result" to the toDisplay object.
  8. We then perform another query on the result object and assign it to a Person object - singlePerson using the equalTo() predicate specying the age field and passing specificAge as the second argument and finally we specify that we want the first object that matches our query with - findFirst() method.
    • The above chained query wil firstly give us a RealmResult list of objects that have their age field greater than the entered age - e.g 30 and then will finally pick one object that has its age equal to the specificAge variable entered by the user and any object that first matches this condition is returned.
  9. We then check if the singlePerson is null because there is a possiblity that no Person object matches our query and if none meets the condition, we append the text - "No Person in our result set has an age of "+specificAge" to the toDisplay object inside our else statment.
  10. If we have a matching object, we append the text - "toDisplay.append("This is the object returned after query chaining");" to the toDisplay object and then we get the details on the object by:
    • name : singlePerson.name
    • phone number : singlePerson.phone_number
    • email : singlePerson.email
    • address : singlePerson.address
    • age : singlePerson.age
  11. We then display our result in our TextView - resultTv.setText(toDisplay.toString());

Application Execution

Sort:  

Thank you for your contribution.
While I liked the content of your contribution, I would still like to extend one advice for your upcoming contributions:

  • Improve the structure of the tutorial.

Looking forward to your upcoming tutorials. Good job!

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]

Thanks @portugalcoin for moderating my contributions.
I would definitely improve on subsequent contributions.

Hey @edetebenezer
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Contributing on Utopian
Learn how to contribute on our website or by watching this tutorial on Youtube.

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Coin Marketplace

STEEM 0.18
TRX 0.14
JST 0.029
BTC 58068.07
ETH 3133.85
USDT 1.00
SBD 2.44