Skip to content

Commit e89d895

Browse files
Merge pull request #183 from CodeForPhilly/feat/zbl-hyperlinkgenus
Implement genus and family filters in plant search; add corresponding…
2 parents 84da937 + f4cc316 commit e89d895

File tree

4 files changed

+355
-57
lines changed

4 files changed

+355
-57
lines changed

lib/server.js

Lines changed: 107 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -176,51 +176,45 @@ module.exports = async function ({ plants, nurseries }) {
176176
const queryRegex = new RegExp(queryLower, 'i');
177177
const sections = [];
178178

179-
// Search Plants by Common Name and Scientific Name
180-
const plantsByCommonName = await plants.find({
181-
"Common Name": queryRegex
182-
})
183-
.sort({ "Recommendation Score": -1 })
184-
.limit(10)
185-
.toArray();
186-
187-
const plantsByScientificName = await plants.find({
188-
"Scientific Name": queryRegex
189-
})
190-
.sort({ "Recommendation Score": -1 })
191-
.limit(10)
192-
.toArray();
179+
// Search Genus
180+
const genusValues = await plants.distinct("Genus");
181+
const matchingGenus = genusValues
182+
.filter(value => value && queryRegex.test(value))
183+
.slice(0, 10);
193184

194-
// Combine and deduplicate plant results
195-
const plantMap = new Map();
196-
plantsByCommonName.forEach(plant => {
197-
plantMap.set(plant._id, {
198-
plantId: plant._id,
199-
commonName: plant["Common Name"],
200-
scientificName: plant["Scientific Name"],
201-
displayText: plant["Common Name"],
202-
subtitle: plant["Scientific Name"],
203-
action: "navigateToPlant"
185+
if (matchingGenus.length > 0) {
186+
sections.push({
187+
type: "filter",
188+
filterName: "Genus",
189+
label: "Genus",
190+
items: matchingGenus.map(value => ({
191+
value: value,
192+
displayText: value,
193+
action: "applyFilter",
194+
filterName: "Genus",
195+
filterValue: value
196+
}))
204197
});
205-
});
206-
plantsByScientificName.forEach(plant => {
207-
if (!plantMap.has(plant._id)) {
208-
plantMap.set(plant._id, {
209-
plantId: plant._id,
210-
commonName: plant["Common Name"],
211-
scientificName: plant["Scientific Name"],
212-
displayText: plant["Common Name"],
213-
subtitle: plant["Scientific Name"],
214-
action: "navigateToPlant"
215-
});
216-
}
217-
});
198+
}
218199

219-
if (plantMap.size > 0) {
200+
// Search Family
201+
const familyValues = await plants.distinct("Family");
202+
const matchingFamily = familyValues
203+
.filter(value => value && queryRegex.test(value))
204+
.slice(0, 10);
205+
206+
if (matchingFamily.length > 0) {
220207
sections.push({
221-
type: "plant",
222-
label: "Plant",
223-
items: Array.from(plantMap.values())
208+
type: "filter",
209+
filterName: "Family",
210+
label: "Family",
211+
items: matchingFamily.map(value => ({
212+
value: value,
213+
displayText: value,
214+
action: "applyFilter",
215+
filterName: "Family",
216+
filterValue: value
217+
}))
224218
});
225219
}
226220

@@ -268,6 +262,54 @@ module.exports = async function ({ plants, nurseries }) {
268262
});
269263
}
270264

265+
// Search Plants by Common Name and Scientific Name (kept last so filters are shown first)
266+
const plantsByCommonName = await plants.find({
267+
"Common Name": queryRegex
268+
})
269+
.sort({ "Recommendation Score": -1 })
270+
.limit(10)
271+
.toArray();
272+
273+
const plantsByScientificName = await plants.find({
274+
"Scientific Name": queryRegex
275+
})
276+
.sort({ "Recommendation Score": -1 })
277+
.limit(10)
278+
.toArray();
279+
280+
// Combine and deduplicate plant results
281+
const plantMap = new Map();
282+
plantsByCommonName.forEach(plant => {
283+
plantMap.set(plant._id, {
284+
plantId: plant._id,
285+
commonName: plant["Common Name"],
286+
scientificName: plant["Scientific Name"],
287+
displayText: plant["Common Name"],
288+
subtitle: plant["Scientific Name"],
289+
action: "navigateToPlant"
290+
});
291+
});
292+
plantsByScientificName.forEach(plant => {
293+
if (!plantMap.has(plant._id)) {
294+
plantMap.set(plant._id, {
295+
plantId: plant._id,
296+
commonName: plant["Common Name"],
297+
scientificName: plant["Scientific Name"],
298+
displayText: plant["Common Name"],
299+
subtitle: plant["Scientific Name"],
300+
action: "navigateToPlant"
301+
});
302+
}
303+
});
304+
305+
if (plantMap.size > 0) {
306+
sections.push({
307+
type: "plant",
308+
label: "Plant",
309+
items: Array.from(plantMap.values())
310+
});
311+
}
312+
271313
res.json({ query, sections });
272314
} catch (error) {
273315
console.error("Autocomplete error:", error);
@@ -446,6 +488,18 @@ module.exports = async function ({ plants, nurseries }) {
446488
value: [],
447489
array: true,
448490
},
491+
{
492+
name: "Genus",
493+
value: [],
494+
array: true,
495+
unwind: false,
496+
},
497+
{
498+
name: "Family",
499+
value: [],
500+
array: true,
501+
unwind: false,
502+
},
449503
{
450504
name: "Sun Exposure Flags",
451505
value: [],
@@ -843,16 +897,20 @@ module.exports = async function ({ plants, nurseries }) {
843897
{
844898
$match: matchQuery,
845899
},
846-
{
900+
];
901+
// Some "array" filters are represented as scalar strings in MongoDB (e.g. Genus/Family).
902+
// Only unwind when the field is actually an array.
903+
if (filter.unwind !== false) {
904+
aQuery.push({
847905
$unwind: `$${filter.name}`,
906+
});
907+
}
908+
aQuery.push({
909+
$group: {
910+
_id: `$${filter.name}`,
911+
count: { $sum: 1 },
848912
},
849-
{
850-
$group: {
851-
_id: `$${filter.name}`,
852-
count: { $sum: 1 },
853-
},
854-
},
855-
];
913+
});
856914
filter.counts = await plants.aggregate(aQuery).toArray();
857915
} else if (filter.boolean) {
858916
const matchQuery = { ...query };

public/assets/images/Family.svg

Lines changed: 8 additions & 0 deletions
Loading

public/assets/images/Genus.svg

Lines changed: 7 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)