Hazelcast Paging Predicates with Example: How to Work in Data Driven Application

Overview

In today’s data-driven world, managing large datasets efficiently is crucial for any application. Hazelcast, a powerful in-memory data grid, provides developers with a robust solution for handling distributed data effectively. Among its many features, Hazelcast Paging Predicates play a pivotal role in efficiently retrieving and processing data subsets.

Hazelcast Paging Predicates with Example: How to Work in Data Driven Application

This article delves deep into the Hazelcast Paging Predicates, explaining what it is, how it works, and how you can leverage it in your applications.

Before exploring Hazelcast Paging Predicates with Example, also visit:

Understanding Paging in Hazelcast

Before we dive into Hazelcast Paging Predicates, it’s essential to understand the concept of paging itself. Paging is a memory management scheme that eliminates the need for contiguous allocation of physical memory and thus eliminates the problems of fitting varying sized memory chunks onto the backing store. In the context of Hazelcast, paging allows you to manage and access large datasets in smaller, manageable chunks.

Why Use Paging?

When dealing with large datasets, loading everything into memory can be inefficient and resource-intensive. Paging helps to:

  • Reduce Memory Footprint: By loading only a subset of data at any given time, applications consume less memory.
  • Improve Performance: Smaller datasets are faster to process and return results, leading to improved application responsiveness.
  • Enhance Scalability: As data grows, paging allows applications to scale efficiently without degrading performance.

What is a Paging Predicate?

A Hazelcast Paging Predicates is a special type of query that allows you to retrieve a specific subset of data based on defined criteria. This predicate not only filters data based on conditions but also manages the pagination of the results. Essentially, it helps you break down the results of a query into smaller, more manageable sets.

Key Features of Paging Predicates

  • Configurable Page Size: You can define how many records to retrieve in each request.
  • Efficient Data Retrieval: It optimizes data access, ensuring minimal memory usage.
  • Flexible Querying: Supports complex queries while managing pagination.

Setting Up Hazelcast

Before using Hazelcast Paging Predicates, you need to set up a Hazelcast instance. Here’s a brief guide on how to do that.

To more about setting up the environment and start the Hazelcast, visit into below articles.

How to Start Hazelcast Members and Client?

Add Hazelcast Dependency

If you are using Maven, include the following dependency in your pom.xml file:

<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast</artifactId>
    <version>5.5.0</version>
</dependency>

Initialize Hazelcast

Create a Hazelcast instance in your Java application as shown below:

// Step 1: Initialize Hazelcast instance
HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance();

Create a Model and Distributed Map

A sample User model class used in demo, create sample User class implemented Serializable and Comparable.

package com.javatecharc.demo.model;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.io.Serializable;

@Setter
@Getter
@AllArgsConstructor
@ToString
public class User implements Serializable, Comparable<User> {
    private int id;
    private String name;
    private String email;
    private int age;

    @Override
    public int compareTo(User user) {
        return this.id - user.id;
    }

    //Create setter, getter or use lombok @Setter, @Getter
}

A distributed map is the primary data structure used in Hazelcast. Here’s how to create one:

// Step 2: Create a distributed map
IMap<Integer, User> userMap = hazelcastInstance.getMap("users");

Create a sample Data

In Hazelcast Paging Predicates example, create sample data and add it into Distributed Map

//Step 3: Create sample data
for (int i = 1; i <= 50; i++) {
    userMap.put(i, new User(100 + i, "User" + i, "user" + i + "@abc.com", 20 + i));
}

In the above sample data creating users 1 to 50 using for loop and adding it into userMap.

Create a Predicate

Create a sample predicate which will filter the data from userMap.

Predicate<Integer, User> equalPredicate = Predicates.greaterEqual("age", 30);

A Descending order comparator (Optional as needed)

However, User class has implemented Comparable interface, by default the all the user added as Ascending order by UserId. But if needed, still you can implement the descending comparator DescendingUserIdComparator.

package com.javatecharc.demo.model;

import java.io.Serializable;
import java.util.Comparator;
import java.util.Map;

public class DescendingUserIdComparator implements Serializable, Comparator<Map.Entry<Integer, User>> {
    @Override
    public int compare(Map.Entry<Integer, User> o1, Map.Entry<Integer, User> o2) {
        User s1 = o1.getValue();
        User s2 = o2.getValue();
        return s2.getId() - s1.getId();
    }
}

Create comparator instance as below before PagingPredicates:

// Default Ascending order, but it can be arranged as a comparator which helps to sort in descending order of id field
Comparator<Map.Entry<Integer, User>> descendingComparator = new DescendingUserIdComparator();

Create instance of PagingPredicate

Create instance of PagingPredicate using descending order comparator and page size.

//Create Paging predicate with page size 10 in default ascending order
PagingPredicate<Integer, User> pagingPredicate = Predicates.pagingPredicate(equalPredicate, 10);

//OR
//Create Paging predicate with page size 10 in descending order
PagingPredicate<Integer, User> pagingPredicate = Predicates.pagingPredicate(equalPredicate, descendingComparator, 10);

Explanation:

  • In previous steps User class implemented Comparable, default order is ascending order. So the pagingPredicate configured with 10 record per page.
  • Also by using second parameter descendingComparator, create the predicate with descending order.

Apply PagingPredicate on Map and Traverse

Apply PagingPredicate on userMap and traverse into pages using nextPage()

//Apply Paging predicate
Collection<User> results = userMap.values(pagingPredicate);

//Next Page
pagingPredicate.nextPage();
results = userMap.values(pagingPredicate);

If you want to fetch any specific page then you can set the page number, first page is always 0, in below we are fetching 4th page.

// since first page is 0, we are requesting the 4th page here
// expected result:
pagingPredicate.setPage(3);
results = userMap.values(pagingPredicate);

Complete PagingPredicate Example

Here how the complete demo code for given example:

package com.javatecharc.demo.predicate;

import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import com.hazelcast.query.PagingPredicate;
import com.hazelcast.query.Predicate;
import com.hazelcast.query.Predicates;
import com.javatecharc.demo.model.DescendingUserIdComparator;
import com.javatecharc.demo.model.User;

import java.util.Collection;
import java.util.Comparator;
import java.util.Map;

public class HazelcastPagingExample {
    public static void main(String[] args) {
        // Step 1: Initialize Hazelcast instance
        HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance();

        // Step 2: Create a distributed map
        IMap<Integer, User> userMap = hazelcastInstance.getMap("users");

        //Step 3: Create sample data
        for (int i = 1; i <= 50; i++) {
            userMap.put(i, new User(100 + i, "User" + i, "user" + i + "@abc.com", 20 + i));
        }

        //Print users
        userMap.values().forEach(System.out::println);

        Predicate<Integer, User> equalPredicate = Predicates.greaterEqual("age", 30);

        // Default Ascending order, but it can be arranged as a comparator which helps to sort in descending order of id field
        Comparator<Map.Entry<Integer, User>> descendingComparator = new DescendingUserIdComparator();

        //Create Paging predicate with page size 10
        PagingPredicate<Integer, User> pagingPredicate = Predicates.pagingPredicate(equalPredicate, descendingComparator, 10);

        //Apply Paging predicate
        Collection<User> results = userMap.values(pagingPredicate);

        //print users
        System.out.println("\nPage 1 -> ");
        results.forEach(System.out::println);


        pagingPredicate.nextPage();
        results = userMap.values(pagingPredicate);
        //print users
        System.out.println("\nPage 2 -> ");
        results.forEach(System.out::println);

        pagingPredicate.nextPage();
        results = userMap.values(pagingPredicate);
        //print users
        System.out.println("\nPage 3 -> ");
        results.forEach(System.out::println);

        // a predicate which fetches 3 user for each page, natural order (see User.compareTo()),
        // does not filter out anything
        pagingPredicate = Predicates.pagingPredicate(3);

        // since first page is 0, we are requesting the 4th page here
        // expected result:
        pagingPredicate.setPage(3);
        results = userMap.values(pagingPredicate);
        System.out.println("\nPage 4 -> ");
        results.forEach(System.out::println);

        System.out.println();

        //Shutdown hazelcast instance
        hazelcastInstance.shutdown();
    }
}

Performance Considerations

While paging predicates offer numerous advantages, it’s essential to keep in mind some performance considerations:

  1. Indexing: Ensure that the fields used in predicates are indexed for optimal performance. This significantly reduces query execution time.
  2. Batch Processing: Instead of fetching all data at once, use batch processing to handle large datasets efficiently.
  3. Monitoring and Tuning: Regularly monitor your Hazelcast performance metrics and adjust configurations to ensure optimal performance.

Conclusion

Hazelcast Paging predicates provide a powerful way to manage and retrieve large datasets efficiently. By leveraging this feature, developers can enhance application performance, reduce memory consumption, and improve scalability. With a solid understanding of how to set up and use paging predicates, you can harness the full potential of Hazelcast in your applications.

Implementing paging predicates not only optimizes data retrieval but also paves the way for a smoother user experience. So, start integrating paging predicates in your Hazelcast projects today, and see the difference it makes in managing your data!

With this comprehensive guide, you should now have a solid foundation for utilizing paging predicates in Hazelcast effectively.

The above example sample code available on github.

Happy coding! 😊

Leave a Comment

Your email address will not be published. Required fields are marked *

Index
Scroll to Top