You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 4 Next »


Goals

  1. User can specified fields in nested object to be indexed. 

  2. Query on these nested fields as well as top level fields. 

 

Out of Scope

  1. Item in collection will not be index and searched.

 

API

  1. Use fieldnameAtLevel1.fieldnameAtLevel2 to specify a field in nested object both for indexing and querying. 

For example, a Customer object contains a Person field. A Person object contains a Page field.

public class Customer implements Serializable {
  private String name;
  private String symbol; // search integer in string format
  private int revenue;
  private int SSN; // search int
  private Person contact; // search nested object 
  ......
}
public class Person implements Serializable {
  private String name;
  private String email;
  private int revenue;
  private String address;
  private Page homepage;
  .......
}
public class Page implements Serializable {
  private int id; // search integer in int format
  private String title;
  private String content;
  final String desc = "At client and server JVM, initializing cache will create the LuceneServiceImpl object," 
     +" which is a singleton at each JVM."; 
  ......
}

 

Example to index on nested fields: following example demos how to index on nested field such as contact.homepage.title, or contact.email, each segment is a field name, not the type name. This will tell the system to find the parent and grandparent field,  because it's possible that several fields will have the same type. For example, Customer class could have 2 Person fields: Person contact and Person deliveryman. The email field to be indexed is from contact field, not deliveryman field. 

// Get LuceneService
LuceneService luceneService = LuceneServiceProvider.get(cache);

// Create Index on fields, some are fields in nested objects:
luceneService.createIndexFactory().addField("name").addField("symbol").addField("revenue").addField("SSN")
      .addField("contact.name").addField("contact.email").addField("contact.address").addField("contact.homepage.title")
      .create("customerIndex", "Customer");

// Now to create region
Region CustomerRegion = ((Cache)cache).createRegionFactory(shortcut).create("Customer");


2.  When querying, the syntax of nested field is the same as toplevel field, i.e. without parent field's name. For example: "email:tzhou12*". 

In order to find the parent document using the child object's field or grandchild object's field (such as use contact's email to find the customer), we need to use lucene's Join package. Geode provides 

LuceneQueryProvider interface to implement such a query. 

 

package org.apache.geode.cache.lucene;

public interface LuceneQueryProvider extends Serializable {

  /**
   * @return A {@link Query} which will be executed against a Lucene index.
   * @param index The {@link LuceneIndex} the query is being executed against.
   * @throws LuceneQueryException if the provider fails to construct the query object. This will be
   *         propagated to callers of the {@link LuceneQuery} find methods.
   */
  public Query getQuery(LuceneIndex index) throws LuceneQueryException;
}


There're several ways in lucene's Join package to search on nested object. https://lucene.apache.org/core/6_3_0/join/org/apache/lucene/search/join/package-summary.html

Such as , such as ToParentBlockJoinQuery, ToChildBlockJoinQuery, JoinUtil. 

In this spec, we provided 2 examples of LuceneQueryProvider implementation using ToParentBlockJoinQuery, one for parent-child join, another for grandparent-parent-child join. 

 

// Example of parent-child join using ToParentBlockJoinQuery
public class ToParentBlockJoinQueryProvider implements LuceneQueryProvider {
  String parentFilterField;
  String parentFilterString;
  String queryOnChild;
  String defaultFieldOnChild;
  
  private transient Query luceneQuery;

  public ToParentBlockJoinQueryProvider(String parentFilterField, String parentFilterString, String queryOnChild, 
      String defaultFieldOnChild) {
    this.parentFilterField = parentFilterField;
    this.parentFilterString = parentFilterString;
    this.queryOnChild = queryOnChild;
    this.defaultFieldOnChild = defaultFieldOnChild;
  }

  @Override
  public Query getQuery(LuceneIndex index) throws LuceneQueryException {
    if (luceneQuery == null) {
      final StandardQueryParser queryParser = new StandardQueryParser(new KeywordAnalyzer());
      Query childQuery = null;
      try {
        childQuery = queryParser.parse(queryOnChild, defaultFieldOnChild);
      }
      catch (QueryNodeException e) {
      }

      BitSetProducer parentFilter = new QueryBitSetProducer(new WildcardQuery(new Term(parentFilterField, parentFilterString)));
      luceneQuery = new ToParentBlockJoinQuery(childQuery, parentFilter, ScoreMode.Total);
    }

    return luceneQuery;
  }
}
 
 
// Search using with nested object using the above LuceneQueryProvider
// It will search child's field "email" for "tzhou11*" and return all the matched parent object, i.e. Customer object
// The parent filter "*" will return all the matched parent object in this example.
ToParentBlockJoinQueryProvider provider = new ToParentBlockJoinQueryProvider("symbol" /*parentField*/, "*" /*parentFilter*/, 
  "email:tzhou11*" /*childQueryString*/, "email" /*childField*/);
LuceneQuery query = luceneService.createLuceneQueryFactory().create("customerIndex", "Customer", provider);
 
PageableLuceneQueryResults<K,Object> results = query.findPages();
  • No labels