package gov.nysenate.openleg.dao.entity.committee.search;
import com.google.common.collect.Lists;
import gov.nysenate.openleg.client.view.committee.CommitteeView;
import gov.nysenate.openleg.dao.base.ElasticBaseDao;
import gov.nysenate.openleg.dao.base.LimitOffset;
import gov.nysenate.openleg.model.base.SessionYear;
import gov.nysenate.openleg.model.entity.*;
import gov.nysenate.openleg.model.search.SearchResults;
import gov.nysenate.openleg.service.entity.committee.data.CommitteeDataService;
import gov.nysenate.openleg.model.entity.CommitteeNotFoundEx;
import gov.nysenate.openleg.util.OutputUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.deletebyquery.DeleteByQueryAction;
import org.elasticsearch.action.deletebyquery.DeleteByQueryRequestBuilder;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.sort.SortBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Repository
public class ElasticCommitteeSearchDao extends ElasticBaseDao implements CommitteeSearchDao {
private static final Logger logger = LoggerFactory.getLogger(ElasticCommitteeSearchDao.class);
private static final String committeeSearchIndexName = "committees";
private static final Pattern committeeSearchIdPattern =
Pattern.compile("(SENATE|ASSEMBLY)-([A-z, ]*)-(.*)");
@Autowired
CommitteeDataService committeeDataService;
@Override
public SearchResults<CommitteeVersionId> searchCommittees(QueryBuilder query, QueryBuilder filter,
List<SortBuilder> sort, LimitOffset limitOffset) {
SearchRequestBuilder searchRequest = getSearchRequest(committeeSearchIndexName, query, filter, sort, limitOffset);
SearchResponse response = searchRequest.execute().actionGet();
return getSearchResults(response, limitOffset, this::getCommitteeVersionId);
}
@Override
public void updateCommitteeIndex(CommitteeSessionId committeeSessionId) {
deleteCommitteeFromIndex(committeeSessionId);
BulkRequestBuilder bulkRequest = searchClient.prepareBulk();
committeeHistoryIndexBulkAdd(committeeSessionId, bulkRequest);
bulkRequest.execute().actionGet();
}
@Override
public void updateCommitteeIndexBulk(Collection<CommitteeSessionId> sessionIds) {
if (sessionIds.isEmpty()) {
return;
}
BulkRequestBuilder bulkRequest = searchClient.prepareBulk();
sessionIds.stream()
.peek(this::deleteCommitteeFromIndex)
.forEach(sessionId -> committeeHistoryIndexBulkAdd(sessionId, bulkRequest));
bulkRequest.execute().actionGet();
}
@Override
public void deleteCommitteeFromIndex(CommitteeSessionId committeeSessionId) {
getCommitteeDeleteRequest(committeeSessionId).execute().actionGet();
}
@Override
protected List<String> getIndices() {
return Lists.newArrayList(committeeSearchIndexName);
}
/**
* --- Internal Methods ---
*/
/**
* Generates a delete request that will delete all indexed committee responses for a specific committee
* for a specific session year
*
* @param committeeSessionId
* @return
*/
protected DeleteByQueryRequestBuilder getCommitteeDeleteRequest(CommitteeSessionId committeeSessionId) {
DeleteByQueryRequestBuilder builder = new DeleteByQueryRequestBuilder(searchClient, DeleteByQueryAction.INSTANCE);
builder.setIndices(committeeSearchIndexName)
.setTypes(Integer.toString(committeeSessionId.getSession().getYear()))
.setQuery(QueryBuilders.boolQuery().must(QueryBuilders.matchAllQuery())
.filter(
QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("chamber", committeeSessionId.getChamber().toString()))
.must(QueryBuilders.termQuery("name", committeeSessionId.getName())) ) );
return builder;
/*return searchClient.prepareDeleteByQuery(committeeSearchIndexName)
.setTypes(Integer.toString(committeeSessionId.getSession().getYear()))
.setQuery(QueryBuilders.filteredQuery( QueryBuilders.matchAllQuery(),
QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("chamber", committeeSessionId.getChamber().toString()))
.must(QueryBuilders.termQuery("name", committeeSessionId.getName())) ) );*/
}
/**
* Adds index requests for all committee versions for a committee session history to the given bulk request
*
* @param committeeSessionId
* @param bulkRequest
*/
protected void committeeHistoryIndexBulkAdd(CommitteeSessionId committeeSessionId, BulkRequestBuilder bulkRequest) {
try {
committeeDataService.getCommitteeHistory(committeeSessionId).stream()
.map(this::getCommitteeVersionIndexRequest)
.forEach(bulkRequest::add);
} catch (CommitteeNotFoundEx ex) {
logger.warn(ExceptionUtils.getStackTrace(ex));
}
}
/**
* Generates an index request for a single committee version
*
* @param committee
* @return
*/
protected IndexRequestBuilder getCommitteeVersionIndexRequest(Committee committee) {
return searchClient.prepareIndex(committeeSearchIndexName,
Integer.toString(committee.getSession().getYear()),
generateCommitteeVersionSearchId(committee.getVersionId()))
.setSource(OutputUtils.toJson(new CommitteeView(committee)));
}
/**
* --- Id Mappers ---
*/
protected String generateCommitteeVersionSearchId(CommitteeVersionId committeeVersionId) {
return generateCommitteeSearchId(committeeVersionId) + "-" +
committeeVersionId.getReferenceDate();
}
protected String generateCommitteeSearchId(CommitteeId committeeId) {
return committeeId.getChamber() + "-" +
committeeId.getName();
}
protected CommitteeVersionId getCommitteeVersionId(SearchHit hit) {
Matcher versionIdMatcher = committeeSearchIdPattern.matcher(hit.getId());
versionIdMatcher.find();
return new CommitteeVersionId(Chamber.getValue(versionIdMatcher.group(1)), versionIdMatcher.group(2),
SessionYear.of(Integer.parseInt(hit.getType())),
LocalDateTime.parse(versionIdMatcher.group(3), DateTimeFormatter.ISO_DATE_TIME));
}
}