Skip to content

Commit

Permalink
mongoose queries and course model
Browse files Browse the repository at this point in the history
  • Loading branch information
marieaurore123 committed Feb 5, 2021
1 parent 9398a7b commit bf18c50
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 7 deletions.
12 changes: 8 additions & 4 deletions _data/bootcamps.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"housing": true,
"jobAssistance": true,
"jobGuarantee": false,
"acceptGi": true
"acceptGi": true,
"averageCost": 10000
},
{
"_id": "5d713a66ec8f2b88b8f830b8",
Expand All @@ -27,7 +28,8 @@
"housing": false,
"jobAssistance": true,
"jobGuarantee": false,
"acceptGi": true
"acceptGi": true,
"averageCost": 12000
},
{
"_id": "5d725a037b292f5f8ceff787",
Expand All @@ -42,7 +44,8 @@
"housing": false,
"jobAssistance": false,
"jobGuarantee": false,
"acceptGi": false
"acceptGi": false,
"averageCost": 8000
},
{
"_id": "5d725a1b7b292f5f8ceff788",
Expand All @@ -62,6 +65,7 @@
"housing": false,
"jobAssistance": true,
"jobGuarantee": true,
"acceptGi": true
"acceptGi": true,
"averageCost": 5000
}
]
109 changes: 107 additions & 2 deletions controllers/bootcamps.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const geocider = require('../utils/geocoder');
const Bootcamp = require('../models/Bootcamp');
const ErrorResponse = require('../utils/errorResponse');
const asyncHandler = require('../middleware/async');
const geocoder = require('../utils/geocoder');
const { remove } = require('../models/Bootcamp');

/**
* NOTES
Expand All @@ -13,10 +16,85 @@ const asyncHandler = require('../middleware/async');
// @route GET /api/v1/bootcamps
// @access Public
exports.getBootcamps = asyncHandler(async (req, res, next) => {
const bootcamps = await Bootcamp.find();

//console.log(req.query); --> obtain all queries from url as a javascript object

/**
* REQ.QUERY EXAMPLES:
* ?location.state=MA&housing=true --> logs as --> { 'location.state': 'MA', housing: 'true' }
* ?averageCost[lte]=1000 --> logs as --> { averageCost: { lte: '1000' } }
*/

let query;

// Copy req.query
const reqQuery = { ...req.query };

// Fields to exclude
const removeFields = ['select', 'sort', 'page', 'limit'];

// Loop thru removeFields and delete them from reqQuery
removeFields.forEach(param => delete reqQuery[param]);

// Create query string
let queryStr = JSON.stringify(reqQuery); // req.query is a JS object --> needs to be a string

// In Mongoose, need: find( { qty: { $gt: 20 } } ) for gt/gte/lt/lte/in --> concatenate a "$" to these values in the req.query
// Note: ?careers[in]=Business --> checks if "Business" is in "careers" array
queryStr = queryStr.replace(/\b(gt|gte|lt|lte|in)\b/g, match => `$${match}`);

// Finding resource
query = Bootcamp.find(JSON.parse(queryStr)); //queryString is a string, needs to be a JS object

// Select Fields ie. ?select=name,description will return only name and description of all entries
if (req.query.select) {

// client will send ?select=name,description but according to Mongoose DB needs to be a string seperated by spaces
// see mongoosejs.com/docs/queries.hyml
const fields = req.query.select.split(',').join(' '); // 'select' is the field
query = query.select(fields); // 'select' is a mongoose function
}

// Sort ie. $sort=name will sort the entires by alphabetical order of names
if (req.query.sort) {
const sortBy = req.query.sort.split(',').join(' ');
query = query.sort(sortBy);
} else {
query = query.sort('-createdAt'); // (-) makes reverse sort
}

// Pagination
const page = parseInt(req.query.page, 10) || 1; // page that client is looking at (default: page 1)
const limit = parseInt(req.query.limit, 10) || 25; // entries per page
const startIndex = (page - 1) * limit; // ie. if on page 3 with limit=2, skip the first (2 * 2 = 4) entries (only display the last 2)
const endIndex = page * limit;
const total = await Bootcamp.countDocuments(); // all objects in the database

query = query.skip(startIndex).limit(limit);

// Executing Query
const bootcamps = await query;

// Pagination result --> sent in res, not saved into DB
const pagination = {};
if (endIndex < total) { // only displays if it is not the last page
pagination.next ={
page: page + 1,
limit
}
}

if (startIndex > 0){ // only displays if it is not the first page
pagination.prev ={
page: page - 1,
limit
}
}

res.status(200).json({
success: true,
count: bootcamps.length,
pagination,
data: bootcamps
})

Expand Down Expand Up @@ -77,11 +155,38 @@ exports.updateBootcamp = asyncHandler(async (req, res, next) => {

// @desc Delete Single Bootcamp
// @route DELETE /api/v1/bootcamps/:id
// @access Provate
// @access Private
exports.deleteBootcamp = asyncHandler(async (req, res, next) => {
const bootcamp = await Bootcamp.findByIdAndDelete(req.params.id);

if (!bootcamp) return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`, 404));

res.status(200).json({success: true, data: {}});
});

// @desc Get bootcamps within a radius
// @route GET /api/v1/bootcamps/radius/:zipcode/:distance
// @access Public
exports.getBootcampsInRadius = asyncHandler(async (req, res, next) => {
const { zipcode, distance } = req.params;

// get lat/long from geocoder
const loc = await geocoder.geocode(zipcode);
const lat = loc[0].latitude;
const lng = loc[0].longitude;

// Calculate radius using radians
// Divide distance by radius of earth
// Earth radius = 3,963 mi / 6,378 km
const radius = distance / 3963;

const bootcamps = await Bootcamp.find({
location: { $geoWithin: { $centerSphere: [ [lng, lat], radius ]} }
});

res.status(200).json({
success: true,
count: bootcamps.length,
data: bootcamps
});
});
4 changes: 4 additions & 0 deletions models/Bootcamp.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ const BootcampSchema = new mongoose.Schema({
}
});

/**
* MONGOOSE MIDDLEWARE
*/

// Create bootcamp slug from the name
BootcampSchema.pre('save', function(next) { // this will run before saving

Expand Down
41 changes: 41 additions & 0 deletions models/Course.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const mongoose = require('mongoose');

const CourseSchema = new mongoose.Schema({
title: {
type: String,
trim: true,
required: [true, 'Please add a course title']
},
description: {
type: String,
required: [true, 'Please add a description']
},
weeks: {
type: String,
required: [true, 'Please add number of weeks']
},
tuition: {
type: Number,
required: [true, 'Please add a tuition cost']
},
minimumSkill: {
type: String,
required: [true, 'Please add a minimum skill'],
enum: ['beginner', 'intermediate', 'advanced']
},
scholarshipAvaliable: {
type: Boolean,
default: false
},
date: {
type: Date,
default: Date.now
},
bootcamp: {
type: mongoose.Schema.ObjectId,
ref: 'Bootcamp', // reference the Bootcamp model
required: true
}
});

module.exports = mongoose.model('Course', CourseSchema);
3 changes: 2 additions & 1 deletion routes/bootcamps.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ const router = express.Router(); // allows api routes to be in different file th

// However, API logic will be in a middleware file (see ./controllers/bootcamps.js)
//1. bring in API names from '../controller/bootcamps
const { getBootcamps, getBootcamp, createBootcamp, updateBootcamp, deleteBootcamp } = require('../controllers/bootcamps')
const { getBootcamps, getBootcamp, createBootcamp, updateBootcamp, deleteBootcamp, getBootcampsInRadius } = require('../controllers/bootcamps')
//2.
router.route('/radius/:zipcode/:distance').get(getBootcampsInRadius);
router.route('/').get(getBootcamps).post(createBootcamp);
router.route('/:id').get(getBootcamp).put(updateBootcamp).delete(deleteBootcamp);

Expand Down
57 changes: 57 additions & 0 deletions seeder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
const fs = require('fs');
const mongoose = require('mongoose');
const colors = require('colors');
const dotenv = require('dotenv');

// Load env variables
dotenv.config({ path: './config/config.env'});

// Load models
const Bootcamp = require('./models/Bootcamp');
const Course = require('./models/Course');

// Connect to DB
mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false,
useUnifiedTopology: true
});

// Read JSON files
// dirname = curernt directory name
const bootcamps = JSON.parse(fs.readFileSync(`${__dirname}/_data/bootcamps.json`, 'utf-8'));
const courses = JSON.parse(fs.readFileSync(`${__dirname}/_data/courses.json`, 'utf-8'));


// Import into DB
const importData = async () => {
try {
await Bootcamp.create(bootcamps);
await Course.create(courses);
console.log('Data Imported...'.green.inverse);
process.exit();
} catch (err) {
console.error(err);
}
}

// Delete Data
const deleteData = async () => {
try {
await Bootcamp.deleteMany();
await Course.deleteMany();
console.log('Data Destroyed...'.red.inverse);
process.exit();
} catch (err) {
console.error(err);
}
}

// When seeder is run, allow argument to know whether to us Import or Delete function
if (process.argv[2] === '-i') { // terminal: node seeder -i
importData();
}
else if (process.argv[2] === '-d'){ // terminal: node seeder -d
deleteData();
}

0 comments on commit bf18c50

Please sign in to comment.