Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ public/gmap.js
run_ff
.gcloudignore
*.log
.firebase
.firebase
secrets.js
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Delish-Food
**Notice**: The API keys for the website are paused as the application is undergoing development for a final release. The current site is hosted with the main branch, one which does not yet incorporate sign-ins (as this branch does). One can still view the site, however, the API calls in place to retrieve restaurants and an unrestricted map view rely on the aforementioned keys, which are paused.

[Delish Food](https://delish-2.web.app/#) is a crowdsourced website designed to be intuitive and visually appealing. Our goal is to help you find the best ethnic food in your area.

https://delish-2.web.app/# \
Expand All @@ -24,4 +26,4 @@ We need your help! When you find a good place to eat, add an upvote to your favo
To run locally: `firebase emulators: start`\
Once everything has been initialized, you will see the domains and ports which the frontend and backend are run on.

WEb app is viewable locally at: http://localhost:5000/#
Web app is viewable locally at: http://localhost:5000/#
39 changes: 39 additions & 0 deletions docs/APICacheDesign.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# API Cache Design

**Overview**: Working with Google API's means paying fractions of a penny for each request. The rates vary depending on which API you choose to work with, however, to improve financial viability of utilizing their services, it would be best practice to save off previously made API calls, such that you never pay for generating the same response twice.

**What calls should be cached?**
- An overview of the Places API calls are available [here](https://developers.google.com/maps/documentation/places/web-service/search).
- Places API requests are made on the server for restaurants near search locale (specified by latitude, longitude) and serve to retrieve information regarding the restaurant's address, website, photos, and other information packaged with the Places API response. There are different versions of Places API requests such as:
- [Text Search](https://developers.google.com/maps/documentation/places/web-service/search-text): This is the search type currently utilized in the `retrieveRestaurants` route within the server at [functions/index.js](../functions/index.js). This type of search provides a plethora of information regarding Places, however, it does not allow for the specification of which fields are desired (a field being an attribute of a Place, e.g. address, place id, address, etc). This can be an issue in that not each field costs the same, and if the fields that are desired are not bounded, you will end up paying for resources which you do not require. `Text Search` returns up to 20 places per response (per page), and up to 60 if a following requests are made with the next page token.
- [Find Place](https://developers.google.com/maps/documentation/places/web-service/search-find-place): This is the search type which allows for the specification of desired fields, however, does not allow for the return of multiple places per request. It also allows for the retrieval of place information by Place ID.

**How "best" to create a cache**:

Utilizing `Find Place` searches in tandem with `Text Search` would be beneficial as new places can be found with `Text Search`, cached in Firestore, and updated with `Find Place` requests once the original request expiration is reached (more on data expriation/cache refresh below). Once the original set of places from `Text Search` are cached in relation to the initial request (a request is indentified by rounded latitude, rounded longitude and cuisine type), subsequent calls to `Find Place` can be made with each places `place_id` so that it is up to date.

**Request Expiration/Cache Refresh**:

Restaurants can go out of business, so it is important that we have an expiration date on how long we serve the same places. As we are subservient to Google in regards to the status of a restaurant (either shut-down or operational), we rely on their data to know whether or not a place we are serving to users is open. So, it would be ideal to run checks on places we have previously found by utilizing `Find Place` requests. The responses from these requests will dictate what we do with the place in our cache. If the place is no longer open, we will purge it from the request it is associated with in the cache, and if it is open, we can verify that we still have the correct metadata for the place (update/verify address, udpate/verify name, update/verify cuisine type, update/verify website etc).

The interval at which we will run updates to the cache can range anywhere from a week to a month. As we rely on Google for the status of a restaurant/place, it would be ideal to know how often they are checking on the status of places they send out. It is uncertain whether Google can verify the status of a place outside of the owner of a place notifying Google that their business has changed operational status. For this reason, the initial expiry date on Places will be set at a month after the initial request. This duration can be revisited if more information about how often Google receives/runs place status updates come to light.

**How is a request identified**:

The requests we are making are indentified by their starting search location (latitude, longitude), and cuisine type. It is unlikely that people utilzing this application, even within nearby areas, will end up searching with the exact latitude longitude of another individual (these values go down to the thousandth, .0001 difference could be just be up the block). For this reason, it is important to group together requests within a certain latitude, longitude range/area. The granularity of these ranges/areas can effect the quality of service, and varying granularities of these ranges may be necessary for different areas population density. For example, a region with sparsely connected towns may not require the same precision as cities like San Francisco or New York where there are significantly more restaurants available and commerce hubs to provide reference to.

In regards to space complexity, rounding latitude and longitude values to the nearest tenth versus the nearest hundreth would result in a significantly more storage requirements. As I mentioned above, utilizing different latitude longitude precisions for different areas based on population would be ideal, but for the scope of this project, may be difficult to implement in practice. One way of accomplishing this could be utilizing another API to determine the city a given search locale is from (based on latitude and longitude), and associating a predefined precision for that request's latitude longitude to round to.
For example, for requests made in San Francisco, we can have the latitude longitude values round to the hundreths place, look in the cache for an identical request made previous, if none found, then store the request's results into the cache. For requests made in Santa Cruz, we can have the latitude longitude values round to the tenth places, look in the cache for an indentical request made previous, if none found, then store the request's results into the cache.

For the purpose of this project, the rounding will attempt to be more precise than necessary (to the hundreths/nearest %.%2), then get optimized over time.

The uppe bound space complexity required for associating requests by their latitude longitude to a certain precision can be calculated by:

Total possible latitude longitude areas = `64800*(1/x)` where `x` is the precision (e.g. x=0.01 to round to the nearest hundreth).

For the purposes of this project, the value of the total possible latitude longitude areas, can then be multiplied by the number of different cuisine searches we can run at each latitude longitude area, as well as how many restaurants are stored for each cuisine type.

This brings the upper bound of storage for the cache equal to `64800*(1/x)*y*z` where `y` is the number of cuisines and `z` is the number of restaurants stored per cuisine.

With `x` equal to `0.02`, `y` equal to 13 (current number of cuisines serviced), and `z` equal to 20 (average number of restaurants retrieved for each `Text Search` request), the upper bound of storage/space complexity comes out to: `842,400,000`.
Of this we could cache `42,120,000` unique requests/responses made to the client. This number is retrieved from `842,400,000/20` which removes space assumed by restaurants per cuisine, and we just count total number of unique requests we can service given that each request is indentified by lat, lng, and cuisine type.
9 changes: 9 additions & 0 deletions docs/DatabaseDesign.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Database Design

**Overview**: The current database utilized for this project is [Google Firestore](https://cloud.google.com/firestore) which is a NoSQL, document-oriented database. The nature of the data being stored is generally regarding restaurants and information specific to the restaurant. For this reason, a document/KVS store available in NoSQL database solutions seems effective for this type of application. SQL/Relational database solutions may be desirable in the future if useful relational queries/associations can be drawn from the data.

**What information is being stored in the database?**

The information being stored in the database is primarily application specific restaurant ratings, a cache for requests made to Google Places API (soon to be implemented), and user attributes (previous ratings, favorited restaurants).

Firestore documents and sub documents of varying types. One of those being collections. The existing collections in our system are `places`, `users`, and soon to be `cache`. `places` is where restaurant ratings and restaurant data is stored (keys equal to place ids, values equal to a places attributes). `users` is where user information and previous ratings are stored (keys equal to place ids and values representing the vote value for that place). `cache` will be where previously made requests will be stored with keys equal to latitude longitude pairs, and sub collections/maps with keys equal to cuisine type. The values for each cuisine type key is lists of place ids representing restaurants that were found in relation to a search for restaurants, of said cuisine type, made at that latitude longitude range.
2 changes: 1 addition & 1 deletion firebase.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"port": 5001
},
"firestore": {
"port": 8080
"port": 8000
},
"database": {
"port": 9000
Expand Down
2 changes: 2 additions & 0 deletions functions/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
// Without parser specification, token errors on => and axios for server /index.js
"parser": "babel-eslint",
"parserOptions": {
// Required for certain syntax usages
"ecmaVersion": 2017
Expand Down
Loading