package gov.nysenate.openleg.service.notification.data;
import com.google.common.collect.Range;
import gov.nysenate.openleg.dao.base.LimitOffset;
import gov.nysenate.openleg.dao.base.PaginatedList;
import gov.nysenate.openleg.dao.base.SortOrder;
import gov.nysenate.openleg.dao.notification.NotificationSearchDao;
import gov.nysenate.openleg.model.notification.Notification;
import gov.nysenate.openleg.model.notification.NotificationType;
import gov.nysenate.openleg.model.notification.RegisteredNotification;
import gov.nysenate.openleg.model.search.SearchException;
import gov.nysenate.openleg.model.search.SearchResults;
import gov.nysenate.openleg.model.search.UnexpectedSearchException;
import gov.nysenate.openleg.service.base.search.ElasticSearchServiceUtils;
import gov.nysenate.openleg.util.DateUtils;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class ElasticNotificationService implements NotificationService {
private static final Logger logger = LoggerFactory.getLogger(ElasticNotificationService.class);
@Autowired protected NotificationSearchDao notificationDao;
/** {@inheritDoc} */
@Override
public RegisteredNotification getNotification(long notificationId) throws NotificationNotFoundException {
Optional<RegisteredNotification> notificationOptional = notificationDao.getNotification(notificationId);
if (notificationOptional.isPresent()) {
return notificationOptional.get();
}
throw new NotificationNotFoundException(notificationId);
}
/** {@inheritDoc} */
@Override
public PaginatedList<RegisteredNotification> getNotificationList(Set<NotificationType> types, Range<LocalDateTime> dateTimeRange,
SortOrder order, LimitOffset limitOffset) throws SearchException {
//Todo figure out why the notificationType term filter doesn't work
// FilterBuilder rangeFilter = FilterBuilders.matchAllFilter();
// FilterBuilder typesFilter = FilterBuilders.matchAllFilter();
// if (dateTimeRange != null && !dateTimeRange.encloses(DateUtils.ALL_DATE_TIMES)) {
// rangeFilter = FilterBuilders.rangeFilter("occurred")
// .from(DateUtils.startOfDateTimeRange(dateTimeRange))
// .to(DateUtils.endOfDateTimeRange(dateTimeRange));
// }
// if (types != null && !types.isEmpty() && !types.contains(NotificationType.ALL)) {
// List<String> coveredTypes = types.stream()
// .map(NotificationType::getCoverage)
// .flatMap(Set::stream)
// .map(NotificationType::toString)
// .collect(Collectors.toList());
// logger.info("{}", coveredTypes);
// typesFilter = FilterBuilders.termsFilter("notificationType", coveredTypes);
// }
// FilterBuilder filter = FilterBuilders.andFilter(rangeFilter, typesFilter);
//
// String sort = String.format("occurred:%s", order != null ? order.toString() : "DESC");
//
// return notificationDao.searchNotifications(QueryBuilders.matchAllQuery(), filter, sort, limitOffset)
// .toPaginatedList();
StringBuilder queryStringBuilder = new StringBuilder();
List<String> coveredTypes = types.stream()
.map(NotificationType::getCoverage)
.flatMap(Set::stream)
.map(NotificationType::toString)
.collect(Collectors.toList());
if (!coveredTypes.isEmpty()) {
queryStringBuilder.append("notificationType:");
if (coveredTypes.size() > 1) {
queryStringBuilder.append("(");
}
boolean firstType = true;
for (String type : coveredTypes) {
if (firstType) {
firstType = false;
} else {
queryStringBuilder.append(" ");
}
queryStringBuilder.append(type);
}
if (coveredTypes.size() > 1) {
queryStringBuilder.append(")");
}
queryStringBuilder.append(" AND ");
}
queryStringBuilder.append("occurred:[")
.append(DateUtils.startOfDateTimeRange(dateTimeRange))
.append(" TO ")
.append(DateUtils.endOfDateTimeRange(dateTimeRange))
.append("]");
String sortString = "occurred:" + order;
return notificationSearch(queryStringBuilder.toString(), sortString, limitOffset).toPaginatedList();
}
/** {@inheritDoc} */
@Override
public SearchResults<RegisteredNotification> notificationSearch(String queryString, String sort, LimitOffset limitOffset) throws SearchException {
if (limitOffset == null) {
limitOffset = LimitOffset.ALL;
}
try {
return notificationDao.searchNotifications(QueryBuilders.queryStringQuery(queryString), QueryBuilders.matchAllQuery(),
ElasticSearchServiceUtils.extractSortBuilders(sort), limitOffset);
} catch (SearchParseException ex) {
throw new SearchException("Invalid query string", ex);
}
catch (ElasticsearchException ex) {
throw new UnexpectedSearchException(ex);
}
}
/** {@inheritDoc} */
@Override
public RegisteredNotification registerNotification(Notification notification) {
try {
return notificationDao.registerNotification(notification);
} catch (ElasticsearchException ex) {
return new RegisteredNotification(notification, -1);
}
}
}