Skip to content
13 changes: 9 additions & 4 deletions src/controllers/locations.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,15 @@ export default {
const taxonomyIds = taxonomyId.split(',');
filterParameters.taxonomyIds = await models.Taxonomy.getAllIdsWithinTaxonomies(taxonomyIds);
}
const limit = pageSize || maxResults;

const offset = pageNumber !== undefined && pageSize !== undefined ?
pageNumber * pageSize : undefined;
const requestedLimit = pageSize || maxResults;
const maxDetailedResults = 200;
const limit = locationFieldsOnly
? requestedLimit
: Math.min(requestedLimit, maxDetailedResults);
Comment thread
Rovack marked this conversation as resolved.
Outdated
Comment thread
Rovack marked this conversation as resolved.
Outdated
Comment thread
Rovack marked this conversation as resolved.
Outdated

const offset = pageNumber !== undefined && limit !== undefined ?
pageNumber * limit : undefined;

const {
locations,
Expand All @@ -252,7 +257,7 @@ export default {
sortBy,
});
const plainLocations = await locations
.map(location => location.get({ plain: true }));
.map(location => (location.get ? location.get({ plain: true }) : location));
const paginationCount = Math.ceil(totalNumLocations / pageSize);

const formattedLocations = plainLocations.map((location) => {
Expand Down
105 changes: 51 additions & 54 deletions src/models/location.js
Original file line number Diff line number Diff line change
Expand Up @@ -297,10 +297,12 @@ module.exports = (sequelize, DataTypes, Op) => {
return sequelize.and(requiredDocumentCondition, notRequiredDocumentCondition);
};

Location.findUniqueLocationIds = async (filterParameters,
Location.findUniqueLocationStubs = async (
filterParameters,
additionalConditions,
originalQueryProps = {},
selectedAttributeForOrderBy, noServices) => {
noServices,
) => {
const queryProps = { order: originalQueryProps.order };
// eslint-disable-next-line prefer-destructuring
const limit = originalQueryProps.limit;
Expand Down Expand Up @@ -371,11 +373,6 @@ module.exports = (sequelize, DataTypes, Op) => {
return Location.findAll({
...queryProps,
where: sequelize.and(..._whereConditions, ...additionalConditions),
attributes: [
sequelize.fn('DISTINCT', sequelize.col('Location.id')),
// For SELECT DISTINCT, ORDER BY expressions must appear in select list.
...(selectedAttributeForOrderBy ? [selectedAttributeForOrderBy] : []),
],
raw: true,
// Not like associations and grouping work perfectly out of the box either though...
// https://github.com/sequelize/sequelize/issues/5481
Expand All @@ -390,14 +387,15 @@ module.exports = (sequelize, DataTypes, Op) => {
sequelize.models.Organization,
sequelize.models.PhysicalAddress,
sequelize.models.Phone,
sequelize.models.EventRelatedInfo,
{
model: sequelize.models.Service,
required: !noServices,
include: [
sequelize.models.Taxonomy,
sequelize.models.HolidaySchedule,
...(areRequiredDocsSpecified ? [sequelize.models.RequiredDocument] : []),
...((openAt && !occasion) ? [sequelize.models.RegularSchedule] : []),
...(occasion ? [sequelize.models.HolidaySchedule] : []),
...(servesZipcode ? [sequelize.models.ServiceArea] : []),
...(shouldJoinEligibilities ? [{
model: sequelize.models.Eligibility,
Expand Down Expand Up @@ -489,16 +487,17 @@ module.exports = (sequelize, DataTypes, Op) => {

].reduce((a, b) => a.concat(b));

// Remove duplicates
locations = Array.from(new Map(searchResults.map(item => [item.id, item])).values());
locations = searchResults;
} else {
locations = await findAll(whereConditions);
}

// apply limit and offset in memory here
locations = locations.slice(offset || 0, limit ? (offset || 0) + limit : undefined);
// Remove duplicates
locations = Array.from(new Map(locations.map(item => [item.id, item])).values())
// apply limit and offset in memory here
.slice(offset || 0, limit ? (offset || 0) + limit : undefined);

return locations.map(location => location.id);
return locations;
};

Location.search = async ({
Expand All @@ -512,7 +511,7 @@ module.exports = (sequelize, DataTypes, Op) => {
sortBy,
noServices,
}) => {
let locationIds;
let locationStubs;
let distance;
let totalNumLocations;
// order is used to specify the attribute referenced in the ORDER BY
Expand Down Expand Up @@ -546,19 +545,18 @@ module.exports = (sequelize, DataTypes, Op) => {
if (radius && position) {
const distanceCondition = sequelize.where(distance, { [Op.lte]: radius });

totalNumLocations = (await Location.findUniqueLocationIds(
totalNumLocations = (await Location.findUniqueLocationStubs(
filterParameters,
[distanceCondition],
)).length;

locationIds = await Location.findUniqueLocationIds(
locationStubs = await Location.findUniqueLocationStubs(
filterParameters,
[distanceCondition].filter(Boolean), {
order,
limit,
offset,
},
selectedAttributeForOrderBy,
noServices,
);

Expand All @@ -568,54 +566,53 @@ module.exports = (sequelize, DataTypes, Op) => {
// However, filtering by window functions requires nested queries, which
// aren't natively supported by sequelize and would require a raw query.
// For now, the simplicity and security of sequelize seems worth the slight performance hit.
if (minResults && locationIds.length < minResults) {
totalNumLocations = (await Location.findUniqueLocationIds(filterParameters, [])).length;
locationIds = await Location.findUniqueLocationIds(filterParameters, [], {
if (minResults && locationStubs.length < minResults) {
totalNumLocations = (await Location.findUniqueLocationStubs(filterParameters, [])).length;
locationStubs = await Location.findUniqueLocationStubs(filterParameters, [], {
order,
limit: minResults,
offset,
}, selectedAttributeForOrderBy, noServices);
}, noServices);
}
} else {
totalNumLocations = (await Location.findUniqueLocationIds(filterParameters, [])).length;
locationIds = await Location.findUniqueLocationIds(filterParameters, [], {
totalNumLocations = (await Location.findUniqueLocationStubs(filterParameters, [])).length;
locationStubs = await Location.findUniqueLocationStubs(filterParameters, [], {
limit,
offset,
order,
}, selectedAttributeForOrderBy);
});
Comment thread
Rovack marked this conversation as resolved.
Outdated
}

const additionalLocationData = locationFieldsOnly ? [
sequelize.models.EventRelatedInfo,
{
model: sequelize.models.Service,
include: [
sequelize.models.HolidaySchedule,
],
},
] : [
sequelize.models.Organization,
sequelize.models.EventRelatedInfo,
{
model: sequelize.models.Service,
include: [
sequelize.models.Taxonomy,
sequelize.models.RequiredDocument,
sequelize.models.HolidaySchedule,
],
},
sequelize.models.Phone,
sequelize.models.PhysicalAddress,
];
const locationIds = locationStubs.map(location => location.id);

const locationsWithAssociations = await Location.findAll({
attributes: {
include: selectedAttributeForOrderBy ? [selectedAttributeForOrderBy] : undefined,
},
where: { id: { [Op.in]: locationIds } },
include: additionalLocationData,
order,
});
let locationsWithAssociations;
if (locationFieldsOnly) {
locationsWithAssociations = locationStubs;
Comment thread
Rovack marked this conversation as resolved.
Comment thread
Rovack marked this conversation as resolved.
} else {
Comment thread
Rovack marked this conversation as resolved.
const additionalLocationData = [
sequelize.models.Organization,
sequelize.models.EventRelatedInfo,
{
model: sequelize.models.Service,
include: [
sequelize.models.Taxonomy,
sequelize.models.RequiredDocument,
sequelize.models.HolidaySchedule,
],
},
sequelize.models.Phone,
sequelize.models.PhysicalAddress,
];

locationsWithAssociations = await Location.findAll({
attributes: {
include: selectedAttributeForOrderBy ? [selectedAttributeForOrderBy] : undefined,
},
where: { id: { [Op.in]: locationIds } },
include: additionalLocationData,
order,
});
}

function sortByLocationIds(a, b) {
return locationIds.indexOf(a.id) - locationIds.indexOf(b.id);
Expand Down
Loading