FindRelevantService

package com.blizzard.meetups.api.service.findrelevant;


import com.blizzard.meetups.api.core.enums.Community;

import com.blizzard.meetups.api.data.MeetupMetaData;

import com.blizzard.meetups.api.domain.Meetup;

import com.blizzard.meetups.api.domain.MeetupRepository;

import com.blizzard.meetups.api.metrics.FindRelevantMetricsService;

import com.blizzard.meetups.api.service.DistanceService;

import com.blizzard.meetups.api.service.endpointcache.EndpointCacheService;

import com.blizzard.meetups.api.util.Timer;

import com.blizzard.meetups.api.web.io.FindRelevantRequestParameters;

import org.jetbrains.annotations.NotNull;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.data.domain.Page;

import org.springframework.data.domain.Pageable;

import org.springframework.data.repository.support.PageableExecutionUtils;

import org.springframework.stereotype.Service;


import java.util.List;

import java.util.function.Function;

import java.util.function.Predicate;


import static com.blizzard.meetups.api.service.endpointcache.EndpointCacheService.EndpointCacheMode.CACHE_MEETUPS;

import static com.blizzard.meetups.api.web.misc.SortComparator.byDistance;

import static java.util.Collections.singletonMap;

import static java.util.stream.Collectors.toList;


@Service

public class FindRelevantService {


private static final Logger LOGGER = LoggerFactory.getLogger(FindRelevantService.class);


private final CachedFindRelevantService cached;

private final UncachedFindRelevantService uncached;

private final DistanceService distanceService;

private final MeetupRepository meetupRepository;

private final FindRelevantMetricsService metricsService;

private final EndpointCacheService endpointCacheService;


public FindRelevantService(CachedFindRelevantService cached,

UncachedFindRelevantService uncached,

DistanceService distanceService,

MeetupRepository meetupRepository,

FindRelevantMetricsService metricsService,

EndpointCacheService endpointCacheService) {

this.cached = cached;

this.uncached = uncached;

this.distanceService = distanceService;

this.meetupRepository = meetupRepository;

this.metricsService = metricsService;

this.endpointCacheService = endpointCacheService;

}


/**

* <pre>

* withinTimeWindow = startTime < minutesAfter && endTime > minutesBefore

* Query logic:

* =======================================================================================================================================================================================================

* |-------------------------------------------------------------TWO------------------------------------------------------------------|

* |-----------------------------------------------ONE-----------------------------------------|

* |---------------ZERO------------------|

* withinTimeWindow AND isCommunityMatch AND matchesAllProperties AND (belongsToOrganizer OR isCheckedIn OR ((nearLocation OR matchesAnyIdentifiers) AND isApprovalStatusMatch AND matchesAllIdentifiers))

* ========================================================================================================================================================================================================

* </pre>

*/

public Page<MeetupMetaData> findRelevantMeetups(FindRelevantRequestParameters frrp, Pageable page) {

metricsService.addAttributesToNewRelicTransaction(singletonMap("findRelevant.service.endpointCacheMode", CACHE_MEETUPS));

Long before = frrp.getMinutesBefore();

Long after = frrp.getMinutesAfter();

Community community = frrp.getCommunity();


Timer frTimer = Timer.begin("01_findMeetups");

List<Meetup> unfiltered = cached.findRelevantMeetups(before, after, community);

frTimer.switchTo("03_filterCachedMeetupsToActuallyRelevant");

List<Meetup> filteredMeetups = unfiltered.stream()

.filter(matches(frrp))

.collect(toList());

List<MeetupMetaData> filteredMetaDatas = filteredMeetups.stream()

.map(toMeetupMetaData(frrp))

.sorted(byDistance)

.skip(page.getOffset())

.limit(page.getPageSize())

.collect(toList());

Page<MeetupMetaData> filteredMeetupMetaDatasPage = PageableExecutionUtils.getPage(filteredMetaDatas, page, filteredMeetups::size);


metricsService.addAttributesToNewRelicTransaction(Timer.asMap(frTimer.done(), "findRelevant.service"));


return filteredMeetupMetaDatasPage;

}


@SuppressWarnings("Duplicates")

public void clearEndpointCache() {

LOGGER.info("FindRelevantService.clearEndpointCache");

cached.clearEndpointCache();

}


@NotNull

private Predicate<Meetup> matches(FindRelevantRequestParameters frrp) {

return meetup -> cached.includedFor(frrp, meetup);

}


@NotNull

private Function<Meetup, MeetupMetaData> toMeetupMetaData(FindRelevantRequestParameters frrp) {

return distanceService.toMeetupMetaData(frrp.getLat(), frrp.getLng());

}


}