basic global search implementation

This commit is contained in:
ashok-conflux
2013-02-26 17:31:18 +05:30
parent eb09a39a8e
commit fdfaab1038
6 changed files with 408 additions and 0 deletions
@@ -0,0 +1,98 @@
package org.mifosplatform.portfolio.search;
import java.util.HashSet;
import java.util.Set;
public class SearchConstants {
public static enum SEARCH_RESPONSE_PARAMETERS {
ENTITY_ID("entityId"), ENTITY_ACCOUNT_NO("entityAccountNo"), ENTITY_EXTERNAL_ID("entityExternalId"), ENTITY_NAME("entityName"), ENTITY_TYPE(
"entityType"), PARENT_ID("parentId"), PARENT_NAME("parentName");
private final String value;
private SEARCH_RESPONSE_PARAMETERS(final String value) {
this.value = value;
}
private static final Set<String> values = new HashSet<String>();
static {
for (SEARCH_RESPONSE_PARAMETERS param : SEARCH_RESPONSE_PARAMETERS.values()) {
values.add(param.value);
}
}
public static Set<String> getAllValues() {
return values;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
public static enum SEARCH_SUPPORTED_PARAMETERS {
QUERY("query"), RESOURCE("resource");
private final String value;
private SEARCH_SUPPORTED_PARAMETERS(final String value) {
this.value = value;
}
private static final Set<String> values = new HashSet<String>();
static {
for (SEARCH_SUPPORTED_PARAMETERS param : SEARCH_SUPPORTED_PARAMETERS.values()) {
values.add(param.value);
}
}
public static Set<String> getAllValues() {
return values;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
public static enum SEARCH_SUPPORTED_RESOURCES {
CLIENT("client"), GROUP("group"), LOAN("loan");
private final String value;
private SEARCH_SUPPORTED_RESOURCES(final String value) {
this.value = value;
}
private static final Set<String> values = new HashSet<String>();
static {
for (SEARCH_SUPPORTED_RESOURCES param : SEARCH_SUPPORTED_RESOURCES.values()) {
values.add(param.value);
}
}
public static Set<String> getAllValues() {
return values;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
}
@@ -0,0 +1,64 @@
package org.mifosplatform.portfolio.search.api;
import java.util.Collection;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.portfolio.search.SearchConstants.SEARCH_RESPONSE_PARAMETERS;
import org.mifosplatform.portfolio.search.data.SearchConditions;
import org.mifosplatform.portfolio.search.data.SearchData;
import org.mifosplatform.portfolio.search.service.SearchReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/search")
@Component
@Scope("singleton")
public class SearchApiResource {
private final Set<String> searchResponseParameters = SEARCH_RESPONSE_PARAMETERS.getAllValues();
private final PlatformSecurityContext context;
private final SearchReadPlatformService searchReadPlatformService;
private final ToApiJsonSerializer<SearchData> toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper;
@Autowired
public SearchApiResource(final PlatformSecurityContext context, final SearchReadPlatformService searchReadPlatformService, final ToApiJsonSerializer<SearchData> toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper) {
this.context = context;
this.searchReadPlatformService = searchReadPlatformService;
this.toApiJsonSerializer = toApiJsonSerializer;
this.apiRequestParameterHelper = apiRequestParameterHelper;
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String searchData(@Context final UriInfo uriInfo, @QueryParam("query") final String query, @QueryParam("resource") final String resource) {
// FIXME - Do we need to validate for permissions
SearchConditions searchConditions = new SearchConditions(query, resource);
final Collection<SearchData> searchResults = this.searchReadPlatformService.retriveMatchingData(searchConditions);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, searchResults, searchResponseParameters);
}
}
@@ -0,0 +1,48 @@
package org.mifosplatform.portfolio.search.data;
import org.mifosplatform.portfolio.search.SearchConstants.SEARCH_SUPPORTED_RESOURCES;
public class SearchConditions {
private final String searchQuery;
private final String searchResource;
private final Boolean clientSearch;
private final Boolean groupSearch;
private final Boolean loanSeach;
public SearchConditions(String searchQueryParam, String searchResource) {
this.searchQuery = searchQueryParam;
this.searchResource = searchResource;
this.clientSearch = (null == searchResource || searchResource.toLowerCase().contains(SEARCH_SUPPORTED_RESOURCES.CLIENT.name().toLowerCase()))? true : false;
this.groupSearch = (null == searchResource || searchResource.toLowerCase().contains(SEARCH_SUPPORTED_RESOURCES.GROUP.name().toLowerCase()))? true : false;
this.loanSeach = (null == searchResource || searchResource.toLowerCase().contains(SEARCH_SUPPORTED_RESOURCES.LOAN.name().toLowerCase()))? true : false;
}
public SearchConditions(String searchQueryParam, String searchResource, Boolean clientSearch, Boolean groupSearch, Boolean loanSeach) {
this.searchQuery = searchQueryParam;
this.searchResource = searchResource;
this.clientSearch = clientSearch;
this.groupSearch = groupSearch;
this.loanSeach = loanSeach;
}
public String getSearchQuery() {
return this.searchQuery;
}
public String getSearchResource() {
return this.searchResource;
}
public Boolean isClientSearch() {
return this.clientSearch;
}
public Boolean isGroupSearch() {
return this.groupSearch;
}
public Boolean isLoanSeach() {
return this.loanSeach;
}
}
@@ -0,0 +1,62 @@
package org.mifosplatform.portfolio.search.data;
public class SearchData {
private final Long entityId;
private final String entityAccountNo;
private final String entityExternalId;
private final String entityName;
private final String entityType;
private final Long parentId;
private final String parentName;
public SearchData(Long entityId, String entityAccountNo, String entityExternalId, String entityName, String entityType, Long parentId, String parentName) {
this.entityId = entityId;
this.entityAccountNo = entityAccountNo;
this.entityExternalId = entityExternalId;
this.entityName = entityName;
this.entityType = entityType;
this.parentId = parentId;
this.parentName = parentName;
}
public Long getEntityId() {
return this.entityId;
}
public String getEntityAccountNo() {
return this.entityAccountNo;
}
public String getEntityExternalId() {
return this.entityExternalId;
}
public String getEntityName() {
return this.entityName;
}
public String getEntityType() {
return this.entityType;
}
public Long getParentId() {
return this.parentId;
}
public String getParentName() {
return this.parentName;
}
}
@@ -0,0 +1,11 @@
package org.mifosplatform.portfolio.search.service;
import java.util.Collection;
import org.mifosplatform.portfolio.search.data.SearchConditions;
import org.mifosplatform.portfolio.search.data.SearchData;
public interface SearchReadPlatformService {
Collection<SearchData> retriveMatchingData(SearchConditions searchConditions);
}
@@ -0,0 +1,125 @@
package org.mifosplatform.portfolio.search.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.portfolio.search.data.SearchConditions;
import org.mifosplatform.portfolio.search.data.SearchData;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class SearchReadPlatformServiceImpl implements SearchReadPlatformService {
private final NamedParameterJdbcTemplate namedParameterjdbcTemplate;
private final PlatformSecurityContext context;
@Autowired
public SearchReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource) {
this.context = context;
this.namedParameterjdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
@Override
public Collection<SearchData> retriveMatchingData(SearchConditions searchConditions) {
AppUser currentUser = context.authenticatedUser();
String hierarchy = currentUser.getOffice().getHierarchy();
SearchMapper rm = new SearchMapper();
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("hierarchy", hierarchy + "%");
params.addValue("search", searchConditions.getSearchQuery());
params.addValue("partialSearch", "%" + searchConditions.getSearchQuery() + "%");
return this.namedParameterjdbcTemplate.query(rm.searchSchema(searchConditions), params, rm);
}
private static final class SearchMapper implements RowMapper<SearchData> {
public String searchSchema(SearchConditions searchConditions) {
String union = " union all ";
String clientExactMatchSql = " (select 'CLIENT' as entityType, c.id as entityId, c.display_name as entityName, c.external_id as entityExternalId, c.account_no as entityAccountNo "
+ " , c.office_id as parentId, o.name as parentName "
+ " from m_client c join m_office o on o.id = c.office_id where o.hierarchy like :hierarchy and (c.account_no like :search or c.display_name like :search or c.external_id like :search)) ";
String clientMatchSql = " (select 'CLIENT' as entityType, c.id as entityId, c.display_name as entityName, c.external_id as entityExternalId, c.account_no as entityAccountNo "
+ " , c.office_id as parentId, o.name as parentName "
+ " from m_client c join m_office o on o.id = c.office_id where o.hierarchy like :hierarchy and (c.account_no like :partialSearch and c.account_no not like :search) or "
+ "(c.display_name like :partialSearch and c.display_name not like :search) or "
+ "(c.external_id like :partialSearch and c.external_id not like :search))";
String loanExactMatchSql = " (select 'LOAN' as entityType, l.id as entityId, l.account_no as entityName, l.external_id as entityExternalId, l.account_no as entityAccountNo "
+ " , c.id as parentId, c.display_name as parentName "
+ " from m_loan l join m_client c on l.client_id = c.id join m_office o on o.id = c.office_id where o.hierarchy like :hierarchy and l.account_no like :search) ";
String loanMatchSql = " (select 'LOAN' as entityType, l.id as entityId, l.account_no as entityName, l.external_id as entityExternalId, l.account_no as entityAccountNo "
+ " , c.id as parentId, c.display_name as parentName "
+ " from m_loan l join m_client c on l.client_id = c.id join m_office o on o.id = c.office_id where o.hierarchy like :hierarchy and l.account_no like :partialSearch and l.account_no not like :search) ";
String groupExactMatchSql = " (select 'GROUP' as entityType, g.id as entityId, g.name as entityName, g.external_id as entityExternalId, NULL as entityAccountNo "
+ " , g.office_id as parentId, o.name as parentName "
+ " from m_group g join m_office o on o.id = g.office_id where o.hierarchy like :hierarchy and g.name like :search) ";
String groupMatchSql = " (select 'GROUP' as entityType, g.id as entityId, g.name as entityName, g.external_id as entityExternalId, NULL as entityAccountNo "
+ " , g.office_id as parentId, o.name as parentName "
+ " from m_group g join m_office o on o.id = g.office_id where o.hierarchy like :hierarchy and g.name like :partialSearch and g.name not like :search) ";
StringBuffer sql = new StringBuffer();
// first include all exact matches
if (searchConditions.isClientSearch()) {
sql.append(clientExactMatchSql).append(union);
}
if (searchConditions.isLoanSeach()) {
sql.append(loanExactMatchSql).append(union);
}
if (searchConditions.isGroupSearch()) {
sql.append(groupExactMatchSql).append(union);
}
// include all matching records
if (searchConditions.isClientSearch()) {
sql.append(clientMatchSql).append(union);
}
if (searchConditions.isLoanSeach()) {
sql.append(loanMatchSql).append(union);
}
if (searchConditions.isGroupSearch()) {
sql.append(groupMatchSql).append(union);
}
sql.replace(sql.lastIndexOf(union), sql.length(), "");
// remove last occurrence of "union all" string
return sql.toString();
}
@Override
public SearchData mapRow(ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long entityId = JdbcSupport.getLong(rs, "entityId");
final String entityAccountNo = rs.getString("entityAccountNo");
final String entityExternalId = rs.getString("entityExternalId");
final String entityName = rs.getString("entityName");
final String entityType = rs.getString("entityType");
final Long parentId = JdbcSupport.getLong(rs, "parentId");
final String parentName = rs.getString("parentName");
return new SearchData(entityId, entityAccountNo, entityExternalId, entityName, entityType, parentId, parentName);
}
}
}