Search Framework Idea – Java


Introduction

Writing code to implement search in application is a repetitive task, and need understanding of query language provided by underlying search engine. Search framework reduces the development effort and hides implementation complexity by leveraging good Object Oriented principles. It helps to make your design extensible and maintainable.

The solution built using following design patterns

  • Builder
  • Factory
  • Template
  • Strategy

 

Architecture

SearchFramework

Core Components

It consist of interfaces defining contract for searcher, and beans defining search request object format.

Code Sample

public interface Searcher {
   T search(SearchCriteria criteria);
}

public class SearchCriteria {
   private int groupCondition;
   private List searchFilters;
}

public class SearchFilter {
   private String fieldName;
   private String fieldValue;
   private int filterCondition;
}

Provider Integration

In this layer we do provider specific implementation, such that it is implemented once and reused.

Sample for JPA Searcher

public abstract class JPASearcher implements Searcher {
   @Autowired
   private PredicateBuilder predicateBuilder;

   protected Specification getSpecification(final SearchCriteria searchCriteria) {
     return new Specification() {

      @Override
       public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery,
 CriteriaBuilder criteriaBuilder) {
         criteriaQuery.distinct(true);
         return predicateBuilder.build(searchCriteria, criteriaBuilder, root);
       }

   };
}

JPA Predicate builder

@Component
public class PredicateBuilder {
   @Autowired
   private PredicateConditionBuilderFactory conditionBuilderFactory;
   
   public Predicate build(SearchCriteria criteria, CriteriaBuilder cb, Root root) {
     …
     for (SearchFilter searchFilter : criteria.getSearchFilters()) {
       …
     }
     if (criteria.getGroupCondition() == GroupCondition.AND) {
       predicate = cb.and(predicates.toArray(new Predicate[predicates.size()]));
     } else {
       predicate = cb.or(predicates.toArray(new Predicate[predicates.size()]));
     }
   return predicate;
   }
}
 

JPA Condition Builder

public interface PredicateConditionBuilder {

 Predicate build(SearchFilter filter, CriteriaBuilder criteriaBuilder, Root root,
 final DistinctJoinHolder joinHolder);

}

Sample Implementation

public class IsNotNullBuilder implements PredicateConditionBuilder {

private final ConditionBuilderHelper builderHelper = ConditionBuilderHelper.INSTANCE;

@Override
public  Predicate build(SearchFilter filter, CriteriaBuilder criteriaBuilder, Root root,
 final DistinctJoinHolder joinHolder) {
    String fieldName = filter.getField().getSearchFieldName();
    GroupCondition groupCondition = filter.getSearchCriteria().getGroupCondition();
    Path path = builderHelper.getPath(fieldName, root, groupCondition, joinHolder);
    return criteriaBuilder.isNotNull(path);
  }
}

JPA Condition Builder Factory

@Component
public class PredicateConditionBuilderFactory {

  private Map builders;

  @PostConstruct
  protected void init() {
     builders = new HashMap();
     builders.put(FilterCondition.EQUAL, new EqualBuilder());
     builders.put(FilterCondition.NOT_EQUAL, new NotEqualBuilder());
     builders.put(FilterCondition.GT, new GreaterThanBuilder());
     builders.put(FilterCondition.LT, new LessThanBuilder());
     builders.put(FilterCondition.EQ_GT, new GreaterThanEqualBuilder());
     builders.put(FilterCondition.EQ_LT, new LessThanEqualBuilder());
     builders.put(FilterCondition.BETWEEN, new BetweenBuilder());
     builders.put(FilterCondition.LIKE, new LikeBuilder());
     builders.put(FilterCondition.IN, new InBuilder());
     builders.put(FilterCondition.IS_NULL, new IsNullBuilder());
     builders.put(FilterCondition.IS_NOT_NULL, new IsNotNullBuilder());
   }

  public PredicateConditionBuilder getBuilder(FilterCondition filterCondition) {
     return builders.get(filterCondition);
  }

}

Search Configuration

It is not used directly by core framework, but needed by various search utilities – for example search criteria builder utility may want to get list of fields supported in application’s basic search, or may be for validation configuration.

Search Utilities

This layer consist of various search supporting utilities –

  • Search criteria builder & validator.
  • Search filter builder & validator.

Simple JPA Searcher implementation (Search Services)

@Component
public class EventSearcher extends JPASearcher {
   @Autowired
   private EventRepository repository;

   @Override
   public List search(SearchCriteria criteria) {
     return repository.findAll(getSpecification(criteria));
   }
}

All you have to do is

  • Extend JPASearcher and provide entity to search.
  • Provide jpa repository for entity.
  • Call search with search criteria.

One thought on “Search Framework Idea – Java

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.