Using a query to search for a task_name, when function to find by id is already implemented

1.1k Views Asked by At

Okay so I'm trying to create a simple todo list, web api. I have the basic functions implemented and working properly but I'm trying to use a query to search by task_name as declared in my code, but no matter what I can't seem to get it functioning.

app.js

var express = require('express')
  , routes = require('./routes')
  , http = require('http')
  , tasks = require('./routes/tasks')
  , mongoose = require('mongoose');


// MongoDB Connection 
mongoose.connect('mongodb://localhost/task_tracker');
var app = express();

app.configure(function(){
  app.set('port', 3000);
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler());
});

app.get('/', routes.index);
app.get('/tasks', tasks.index);
app.get('/tasks/:id', tasks.show);
//app.get('/tasks/tasks?', tasks.search);
app.get('/tasks?', tasks.search);


app.post('/tasks', tasks.create);
app.put('/tasks', tasks.update);
app.del('/tasks', tasks.delete);

http.createServer(app).listen(app.get('port'), function(){
  console.log("Express server listening on port 3000");
});

tasks.js

var Task = require('../models/task').Task; 

/*
 * Tasks Routes
 */
exports.index = function(req, res) {
  Task.find({}, function(err, docs) {
    if(!err) {
      res.json(200, { tasks: docs });  
    } else {
      res.json(500, { message: err });
    }
  });
}

exports.show = function(req, res) {

  var id = req.params.id; 
  Task.findById(id, function(err, doc) {
    if(!err && doc) {
      res.json(200, doc);
    } else if(err) {
      res.json(500, { message: "Error loading task." + err});
    } else {
      res.json(404, { message: "Task not found."});
    }
  });
}

exports.create = function(req, res) {

  var task_name = req.body.task_name; // Name of task. 
  var description = req.body.task_description;  // Description of the task

  //Task.findOne({ name: task_name }, function(err, doc) {  // This line is case sensitive.
  Task.findOne({ name: { $regex: new RegExp(task_name, "i") } }, function(err, doc) {  // Using RegEx - search is case insensitive
    if(!err && !doc) {

      var newTask = new Task(); 

      newTask.name = task_name; 
      newTask.description = description; 

      newTask.save(function(err) {

        if(!err) {
          res.json(201, {message: "Task created with name: " + newTask.name });    
        } else {
          res.json(500, {message: "Could not create task. Error: " + err});
        }

      });

    } else if(!err) {

      // User is trying to create a task with a name that already exists. 
      res.json(403, {message: "Task with that name already exists, please update instead of create or create a new task with a different name."}); 

    } else {
      res.json(500, { message: err});
    } 
  });

}

exports.update = function(req, res) {

  var id = req.body.id; 
  var task_name = req.body.task_name;
  var task_description = req.body.task_description; 

  Task.findById(id, function(err, doc) {
      if(!err && doc) {
        doc.name = task_name; 
        doc.description = task_description; 
        doc.save(function(err) {
          if(!err) {
            res.json(200, {message: "Task updated: " + task_name});    
          } else {
            res.json(500, {message: "Could not update task. " + err});
          }  
        });
      } else if(!err) {
        res.json(404, { message: "Could not find task."});
      } else {
        res.json(500, { message: "Could not update task." + err});
      }
    }); 
}

exports.delete = function(req, res) {

  var id = req.body.id; 
  Task.findById(id, function(err, doc) {
    if(!err && doc) {
      doc.remove();
      res.json(200, { message: "Task removed."});
    } else if(!err) {
      res.json(404, { message: "Could not find task."});
    } else {
      res.json(403, {message: "Could not delete task. " + err });
    }
  });
}

exports.search = function(req, res) {
    var name = req.query.name;
    Task.findByName(name, function(err, doc) {
          if(!err && doc) {
            res.json(200, doc);
          } else if(err) {
            res.json(500, { message: "Error loading task." + err});
          } else {
            res.json(404, { message: "Task not found."});
          }
        });
      }

task.js model

var mongoose = require('mongoose')
  , Schema = mongoose.Schema;

var taskSchema = new Schema({
    name          : { type: String, required: true, trim: true, index: { unique: true } }
  , description   : { type: String, required: true }
  , date_created  : { type: Date, required: true, default: Date.now }
});

var task = mongoose.model('task', taskSchema);

module.exports = {
  Task: task
};

Basically i am just trying to use a similar function to that of my search by id function but i know i can't just use parameters and I can't figure out how to get the query working. Any help would be appreciated. If you can't tell I'm using Node.js, Express and Mongodb.

2

There are 2 best solutions below

0
On BEST ANSWER

TL;DR: You need to merge tasks.index and tasks.search route, ie. like this:

tasks.index = function(req, res, next) {

  if (req.query.name !== undefined) {
    // pass on to next handler
    return next();
  }
  // the rest of your tasks.index.
});

And adjust the Router setup like this:

app.get('/tasks', tasks.index);
app.get('/tasks', tasks.search);

Why? Query string is not part of the route. So '/tasks?' is just a regex for /tasks+1 character, but not for a query string - query string is not a part of the route match.

More specifically, you have in your routes this:

app.get('/', routes.index);
app.get('/tasks', tasks.index);
app.get('/tasks?', tasks.search);

That last, /tasks? route will not get registered like you seem to expect. The question mark isn't representing query string processing, it's a part of the route regex, and basically means that you'd catch anything that adds one character to /tasks route, ie /tasksa, /tasksb, /tasks7 etc. So, 7 characters, first six of which are known, the last is different, query string not included.

You cannot parse query strings in the router, it's in the individual controllers, kind of like this:

tasks.search = function(req, res) {
  if (req.query.name) {
    // you have the name query
  }
  // etc.
}

Additional advice is, what is usually done on a REST API is have the global tasks.index, like you have there, and add two things on it: paging and filter/searching. If you want just one result

Paging is page=3&limit=10 (3rd page, 10 items per page), and filtering/sorting/searching is what you want. And depending how you want it, that's how you expose it.

Ie. you might want to sort by name:

if (req.query.sort === 'name:desc') {

  mongoCursor.sort = {name: -1};
}

Or something of a sort.

So you'd probably have a search, or maybe directly a name query parameter, like this:

GET /tasks?name=<search term>

And the name param is usually optional. So your req would list all things, and if name query string is set, it would filter by name first.

Your query building process can then look like this:

tasks.index = function(req, res) {

var query = {};
if (req.query.name) {
    query.name = req.query.name;
}
Tasks.find(query, ...);

In that case, you don't need helpers on the Task model.

0
On

I found this method also works.

/**
 * Module dependencies.
 */

var express = require('express'),
    cors = require('cors'),
    routes = require('./routes'),
    http = require('http'),
    tasks = require('./routes/tasks'),
    mongoose = require('mongoose'),
    search = require('./routes/search');
var Task = require('./models/task').Task; 




// MongoDB Connection 
mongoose.connect('mongodb://localhost/task_tracker');
var app = express();



        app.configure(function() {
            app.set('port', 3000);
            app.set('views', __dirname + '/views');
            app.set('view engine', 'jade');
            app.use(express.favicon());
            app.use(express.logger('dev'));
            app.use(express.bodyParser());
            app.use(express.methodOverride());
            app.use(app.router);
            app.use(express.urlencoded());
            app.use(express.json());
            app.use(cors());

        });
        app.use(function(req, res, next) {
          res.header("Access-Control-Allow-Origin", "*");
          res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
          next();
        });

        var corsOptions = {
          origin: 'http://localhost:3000'
      };


        app.get('/', routes.index);
        app.get('/tasks', tasks.index);
        //app.get('/search', tasks.FindByQuery);
        //app.get('/tasks/:task.:name?', task.FindByQuery); 
        app.get('/search', function(req, res, next) {
            var query = req.query
            //res.send(query['name']);
            Task.findOne({name: query['name']}, function(err, doc) {
                if(!err && doc) {
                    res.json(200, doc);
                } else if(err) {
                    res.json(500, { message: "Error loading task." + err});
                } else {
                    res.json(404, { message: "Task not found."});
                }
            });
            //res.end(JSON.stringify(query));
        });
        app.get('/tasks/:id', tasks.show);
        app.post('/tasks', tasks.create);
        app.put('/tasks', tasks.update);
        app.del('/tasks', tasks.delete);

        http.createServer(app).listen(app.get('port'), function() {
            console.log("Express server listening on port 3000");

    });