1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { bearerAuth } from "npm:hono/bearer-auth";
import { cors } from "npm:hono/cors";
import { Hono } from "npm:hono@3";
import Typesense from "npm:typesense";
import type { SearchParams, SearchParamsWithPreset, SearchResponseHit } from "npm:typesense/lib/Typesense/Documents";
const MAX_SEARCH_RADIUS_IN_MILES = 10;
const COLLECTIONS = {
"resources": "resources",
};
const typesenseClient = new Typesense.Client({
"nodes": [{
"host": Deno.env.get("TYPESENSE_HOST") || "", // For Typesense Cloud use xxx.a1.typesense.net
"port": 443, // For Typesense Cloud use 443
"protocol": "https", // For Typesense Cloud use https
}],
"apiKey": Deno.env.get("TYPESENSE_API_KEY") || "",
"connectionTimeoutSeconds": 10,
"retryIntervalSeconds": 0.1,
"healthcheckIntervalSeconds": 2,
"logLevel": "debug",
});
interface Resource {
id: string;
name: string;
address: {
full_address: string;
latitude: number;
longitude: number;
};
website: string;
phone: string;
user_email: string;
description: string;
utc_offset: number;
featured_image_url: string;
hours_text: string | null;
hours: {
day: string;
day_int: number;
open_time: string | null;
close_time: string | null;
}[];
services: {
id: string | number;
name: string;
description: string;
category: {
icon: string;
name: ResourceTypes;
};
}[];
qualifications: {
id: string | number;
name: string;
}[];
// Custom field added by Cameron Pak to calculate on the fly.
distance_between: number;
}
const CATEGORY_IDS_FROM_TYPESENSE = {
// Favorites does not exist in Typesense but is a ResourceTypes type.
"Favorites": -1,
"Food": 53,
"Shelter": 52,
"Churches": 56,
"Resources": 57,
"Medical": 54,
"Addiction Recovery": 58,
"Domestic Violence": 51,
"Housing": 69,
};
type ResourceTypes = keyof typeof CATEGORY_IDS_FROM_TYPESENSE;
/**
* Converts meters to miles.
*
* @param {number} meters - the distance in meters to convert
* @return {number} the distance in miles
*/
function convertMetersToMiles(meters: number): number {
return 0.000621371 * meters;
}
// Round a number to the nearest quarter.
function roundToQuarter(number: number) {
let remainder = number % 1;
// I only want 1/8th of a mile to appear when when that's the only remainder.
if (number < 1 && remainder <= 0.125) {
return Math.floor(number) + 0.125;
} else if (remainder <= 0.375) {
return Math.floor(number) + 0.25;
} else if (remainder <= 0.625) {
return Math.floor(number) + 0.50;
} else if (remainder <= 0.875) {
return Math.floor(number) + 0.75;
} else {