Apache Geode CHANGELOG

Querying a Partitioned Region on a Single Node

To direct a query to specific partitioned region node, you can execute the query within a function. Use the following steps:

  1. Implement a function which executes a query using RegionFunctionContext.

    /**
     * This function executes a query using its RegionFunctionContext
     * which provides a filter on data which should be queried.
     *
     */
    public class MyFunction extends FunctionAdapter {
    
        private final String id;
    
        @Override
        public void execute(FunctionContext context) {
    
          Cache cache = CacheFactory.getAnyInstance();
          QueryService queryService = cache.getQueryService();
    
          String qstr = (String) context.getArguments();
    
          try {
            Query query = queryService.newQuery(qstr);
    
            //If function is executed on region, context is RegionFunctionContext
            RegionFunctionContext rContext = (RegionFunctionContext)context;
    
            SelectResults results = (SelectResults) query.execute(rContext)
    
            //Send the results to function caller node.
            context.getResultSender().sendResult((ArrayList) (results).asList());
            context.getResultSender().lastResult(null);
    
          } catch (Exception e) {
            throw new FunctionException(e);
          }
        }
    
        @Override
        public boolean hasResult() {
          return true;
        }
    
        @Override
        public boolean isHA() {
          return false;
        }
    
        public MyFunction(String id) {
          super();
          this.id = id;
        }
    
        @Override
        public String getId() {
          return this.id;
        }
      }
    
  2. Decide on the data you want to query. Based on this decision, you can use PartitionResolver to configure the organization of buckets to be queried in the Partitioned Region.

    For example, let’s say that you have defined the PortfolioKey class:

    public class PortfolioKey implements DataSerializable {
      private int id;
      private long startValidTime;
      private long endValidTime
      private long writtenTime
    
      public int getId() {
        return this.id;
      }
    ...
    }
    

    You could use the MyPartitionResolver to store all keys with the same ID in the same bucket. This PartitionResolver has to be configured at the time of Partition Region creation either declaratively using xml OR using APIs. See Configuring Partitioned Regions for more information.

    /** This resolver returns the value of the ID field in the key. With this resolver, 
     * all Portfolios using the same ID are colocated in the same bucket.
     */
    public class MyPartitionResolver implements PartitionResolver, Declarable {
    
       public Serializable getRoutingObject(EntryOperation operation) {
       return operation.getKey().getId();
    }
    
  3. Execute the function on a client or any other node by setting the filter in the function call.

    /**
     * Execute MyFunction for query on specified keys.
     *
     */
    public class TestFunctionQuery {
    
      public static void main(String[] args) {
    
        ResultCollector rcollector = null;
        PortfolioKey portfolioKey1 = ...;
    
        //Filter data based on portfolioKey1 which is the key used in 
        //region.put(portfolioKey1, portfolio1);
        Set filter = Collections.singleton(portfolioKey1);
    
        //Query to get all positions for portfolio ID = 1
        String qStr = "SELECT positions FROM /myPartitionRegion WHERE ID = 1";
    
        try {
          Function func = new MyFunction("testFunction");
    
          Region region = CacheFactory.getAnyInstance().getRegion("myPartitionRegion");
    
          //Function will be routed to one node containing the bucket
          //for ID=1 and query will execute on that bucket.
          rcollector = FunctionService
              .onRegion(region)
              .setArguments(qStr)
              .withFilter(filter)
              .execute(func);
    
          Object result = rcollector.getResult();
    
          //Results from one or multiple nodes.
          ArrayList resultList = (ArrayList)result;
    
          List queryResults = new ArrayList();
    
          if (resultList.size()!=0) {
            for (Object obj: resultList) {
              if (obj != null) {
                queryResults.addAll((ArrayList)obj);
              }
            }
          }
          printResults(queryResults);
    
        } catch (FunctionException ex) {
            getLogger().info(ex);
        }
      }
    }