How can i parse express query param to mongodb regex query? - regex

I try to build a test backend with express.
And got a problem that i cannot seem to overcome.
Trying to give the query object $regex param a value from the req.query.name. But it only returns an empty array. When i console.log(laptopName) it outputs the right value that is being given as a query param.
If i just replace $regex: laptopName with a concrete value $regex: 'vivobook' i do get results.
With $regex: laptopName console.log looks like this { name: { '$regex': '"vivobook"', '$options': 'i' } }
With $regex: 'vivobook' console.log looks like this: { name: { '$regex': 'vivobook', '$options': 'i' } }
app.get("/api/v1/laptops", (req, res) => {
MongoClient.connect(mongo_url, { useNewUrlParser: true }, (err, client) => {
const db = client.db("laptop_backend");
const collection = db.collection("laptops");
let laptopName = req.query.name;
let query = {
name: {
$regex: laptopName,
$options: 'i'
}
};
collection
.find(query)
.toArray()
.then(response => res.status(200).json(response))
.catch(err => console.error(err));
});
});

Try getting laptop name directly from params such as
let { name } = req.query;
let query = {
name: {
$regex: name,
$options: 'i',
}
};
if you are to modify completely i suggest changing code without promise.
app.get("/api/v1/laptops", async (req, res) => {
try {
const client = await MongoClient.connect(mongo_url, { useNewUrlParser: true });
const db = client.db('laptop_backend');
const collection = db.collection('laptops');
const { name } = req.query;
const query = {
name: {
$regex: name,
$options: 'i',
},
};
const result = await collection.find(query);
res.status(200).json({ result });
} catch (e) {
console.log(e);
res.status(400).json({ error: true });
}
});

Related

API request getting stuck in POSTMAN?

So, I am making an e-shop app which uses Mongo DB and Express JS as the backend. I have already created the productSchema, userSchema and the categorySchema and have coded for the appropriate GET requests.
I have made a jwt.js file which handles whether the the GET request should be allowed or not based on the token.
The code for jwt.js is given below
const { expressjwt } = require("express-jwt");
function authJwt() {
const secret = process.env.secret;
const api = process.env.API_URL;
return expressjwt({
secret,
algorithms: ["HS256"],
isRevoked: isRevoked,
}).unless({
path: [
{ url: /\/api\/v1\/products(.*)/, methods: ["GET", "OPTIONS"] },
{ url: /\/api\/v1\/categories(.*)/, methods: ["GET", "OPTIONS"] },
`${api}/users/login`,
`${api}/users/register`,
],
});
}
async function isRevoked(req, payload, done) {
if (!payload.isAdmin) {
done(null, true);
}
done();
}
module.exports = authJwt;
The code for products.js which handles the GET, POST, PUT and DELETE requests for the products database is given below.
const { Product } = require("../models/product");
const express = require("express");
const { Category } = require("../models/category");
const router = express.Router();
const mongoose = require("mongoose");
router.get(`/`, async (req, res) => {
// localhost:3000/api/v1/products?categories=2342342,234234
let filter = {};
if (req.query.categories) {
filter = { category: req.query.categories.split(",") };
}
const productList = await Product.find(filter).populate("category");
if (!productList) {
res.status(500).json({ success: false });
}
res.send(productList);
});
router.get(`/:id`, async (req, res) => {
const product = await Product.findById(req.params.id).populate("category");
if (!product) {
res.status(500).json({ success: false });
}
res.send(product);
});
router.post(`/`, async (req, res) => {
const category = await Category.findById(req.body.category);
if (!category) return res.status(400).send("Invalid Category");
let product = new Product({
name: req.body.name,
description: req.body.description,
richDescription: req.body.richDescription,
image: req.body.image,
brand: req.body.brand,
price: req.body.price,
category: req.body.category,
countInStock: req.body.countInStock,
rating: req.body.rating,
numReviews: req.body.numReviews,
isFeatured: req.body.isFeatured,
});
product = await product.save();
if (!product) return res.status(500).send("The product cannot be created");
res.send(product);
});
router.put("/:id", async (req, res) => {
if (!mongoose.isValidObjectId(req.params.id)) {
return res.status(400).send("Invalid Product Id");
}
const category = await Category.findById(req.body.category);
if (!category) return res.status(400).send("Invalid Category");
const product = await Product.findByIdAndUpdate(
req.params.id,
{
name: req.body.name,
description: req.body.description,
richDescription: req.body.richDescription,
image: req.body.image,
brand: req.body.brand,
price: req.body.price,
category: req.body.category,
countInStock: req.body.countInStock,
rating: req.body.rating,
numReviews: req.body.numReviews,
isFeatured: req.body.isFeatured,
},
{ new: true }
);
if (!product) return res.status(500).send("the product cannot be updated!");
res.send(product);
});
router.delete("/:id", (req, res) => {
Product.findByIdAndRemove(req.params.id)
.then((product) => {
if (product) {
return res
.status(200)
.json({ success: true, message: "the product is deleted!" });
} else {
return res
.status(404)
.json({ success: false, message: "product not found!" });
}
})
.catch((err) => {
return res.status(500).json({ success: false, error: err });
});
});
router.get(`/get/count`, async (req, res) => {
const productCount = await Product.countDocuments((count) => count);
if (!productCount) {
res.status(500).json({ success: false });
}
res.send({
productCount: productCount,
});
});
router.get(`/get/featured/:count`, async (req, res) => {
const count = req.params.count ? req.params.count : 0;
const products = await Product.find({ isFeatured: true }).limit(+count);
if (!products) {
res.status(500).json({ success: false });
}
res.send(products);
});
module.exports = router;
Now, the codes for the users.js and categories.js are similar and thus I am not sharing it.
I am getting the problem when doing GET request for products using POSTMAN API. Even though I am passing the correct token using BEARER TOKEN field in the POSTMAN API, it is getting stuck at sending request. When I delete the isRevoked part, everything works fine, but then again I can't control the get request based on the isAdmin part. So, the problem is in the isRevoked part. But, what exactly is the issue. It seems fine to me logically.
the problem could arise from so many things, could not say without a deeper look at your code but, here are some suggestions:
should isRevoked be async?
does your payload contains isAdmin?
and if so, inside the if statement should be done(null, false) after the if statement you should get a userid or any sort of unique fields such as userEmail, ..., then use your userModel to query the user document so that your last done() be done(null, user)

DynamoDB SDK async function returns undefined when using sinon

I am trying to use sinon to test a piece of code that is using an DynamoDB SDK method batchGet. Below the code:
const fetchSingleUser = async (userId) => {
try {
let queryParams = {RequestItems: {}};
queryParams.RequestItems['users'] = {
Keys: [{'UserId': userId}],
ProjectionExpression: 'UserId,Age,#UserName',
ExpressionAttributeNames: {'#UserName': 'Name'}
};
const res = await docClient.batchGet(queryParams).promise();
return res.Responses.users[0];
} catch (e) {
console.log('users::fetch::error - ', e);
}
};
Below the test using sinon:
'use strict';
const sinon = require('sinon');
const proxyquire = require('proxyquire').noCallThru();
let assert = require('assert');
describe('DynamoDB Mock Test', function () {
let AWS;
let scriptToTest;
let batchGetFunc;
before(function () {
batchGetFunc = sinon.stub();
AWS = {
DynamoDB: {
DocumentClient: sinon.stub().returns({
batchGet: batchGetFunc
})
}
};
scriptToTest = proxyquire('../index', {
'aws-sdk': AWS
});
});
it('Should scan using async/await and promise', async function () {
let result = { UserId: 'segf876seg876', Age: 33, Name: 'Paul' }
batchGetFunc.withArgs(sinon.match.any).returns({
promise: () => result
});
const data = await scriptToTest.fetchSingleUser('segf876seg876');
console.log('--data: ', data)
assert.equal(data.UserId, 'segf876seg876');
});
});
The Problem:
const data = await scriptToTest.fetchSingleUser('segf876seg876') always returns 'undefined'
Function fetchSingleUser always returns 'undefined' because you do not return anything after catch (after error happens). You only define return value on success.
But why errors happens, because const res does not contain Responses.users[0].
Simple solution:
change let result = { UserId: 'segf876seg876', Age: 33, Name: 'Paul' } to satisfy code Responses.users[0] to
const result = {
Responses: {
users: [{ UserId: 'segf876seg876', Age: 33, Name: 'Paul' }],
},
};
Note: use const if you not change variable value.

What's the right way to use excludeFromIndexes in an Embedded Entity in Datastore

Datastore will not allow properties over 1500 bytes in size if they are indexed. So if I have an object
{foo : X, bar : Y}
where Y is longer than 1500 characters I can disable indexing on the individual property and store it by means of an array, thus:
[
{ name : 'foo', value: 'X'},
{ name: 'bar', value: 'Y', excludeFromIndexes: true}
]
But this does not work if the property is part of an Embedded Entity (i.e. an entity inside a property of another entity).
How do I store something like this?
{ foo : X, bar : { baz : Y } }
This does not work:
[
{ name : 'foo', value: 'X'},
{ name: 'bar', value:
{
name: 'baz',
value: 'Y',
excludeFromIndexes: true
},
excludeFromIndexes: true}
]
And this either:
[
{ name : 'foo', value: 'X'},
{ name: 'bar', value: {'baz', 'Y' }, excludeFromIndexes: true}
]
UPDATE:
Here it is an example snippet:
const DataStore = require('#google-cloud/datastore');
const datastore = DataStore({projectId});
const foo1 = { name : { forename: 'Dave', surname : 'Tong' }, colour : 'blue'}
const putAndGet = async data => {
return new Promise(async (resolve, reject) => {
try {
const key = await datastore.save({key: datastore.key([Kind]), data: data});
} catch (err) {
reject(err);
}
const results = [];
const query = datastore.createQuery(Kind);
query.runStream()
.on('error', (error) => {
reject(new Error(error));
})
.on('data', (entity) => {
results.push(entity);
})
.on('end', () => {
resolve(results);
});
});
}
// This will succeed
putAndGet(foo1).then(ret => {
for (var i = 0; i < ret.length; i++) {
console.log(ret[i].name.forename + " likes " + ret[i].colour);
}
const str = [];
for (var i = 0; i < 400; i++) str[i] = 'X';
const foo2 = {name: {forename: str.join('XXX'), surname: 'Tong'}, colour: 'blue'}
// This will fail
return putAndGet(foo2);
}).then(ret => {
for (var i = 0; i < ret.length; i++) {
console.log(ret[i].name.forename + " likes " + ret[i].colour);
}
}).catch(err => {
console.log(err.message);
});
After some testing, I was able to achieve what you want to do. I have also edited your question, in order to make it clearer for future readers of this post, as you refer to "property part of another property", but that is defined in Datastore as an EmbeddedEntity, so I will stick to that name for a clearer explanation.
Embedded entities can have subproperties which are longer than 1500 Bytes, but you must exclude those subproperties explicitly, or an error will show. To do so, you must declare each EmbeddedEntity as:
{
"properties": {
"surname": {
"stringValue": "A long surname which has more than 1500B",
"excludeFromIndexes": true
},
"forename": {
"stringValue": "David"
}
}
}
But declaring the "excludeFromIndexes": true programatically using NodeJS is not too straight-forward. However finally I managed to solve it. The key is the save() function, where you can declare the properties and subproperties that you want to exclude from indexing, like:
datastore.save({key: entity_key, data: entity_data, excludeFromIndexes: ['prop1', 'prop2.subprop1']});
Here I share a small piece of code (which is an MCV from the piece of code that you shared) that works and creates an entity with some properties containing long suproperties:
const DataStore = require('#google-cloud/datastore');
const projectId = "YOUR_PROJECT_ID";
const datastore = DataStore({projectId});
const data = {name: {forename: 'David', surname: '<YOUR_LONG_STRING>'}, colour: 'purple'}
const Kind = "<YOUR_ENTITY_KIND>";
datastore
.save({key: datastore.key([Kind]), data: data, excludeFromIndexes: ['name.surname']})
.then(() => {
console.log(`Entity saved`);
})
.catch(err => {
console.error('ERROR:', err);
});
Once you run this piece of code, if you inspect the entity in your Datastore dashboard, you will be able to see that your EmbeddedEntity is defined as I shared at the beginning of my answer.

How to use graph API with react-native-fbsdk?

I read the document, both on github and Facebook developers docs.
There is only sample, nothing more. No API document.
The code to make a Graph API request is
const infoRequest = new GraphRequest(
'/me',
null,
this._responseInfoCallback,
);
And the callback
_responseInfoCallback(error: ?Object, result: ?Object) {
if (error) {
alert('Error fetching data: ' + error.toString());
} else {
alert('Success fetching data: ' + result.toString());
}
}
And here is the function to make a Graph API request
testRequestGraphAPI(){
const infoRequest = new GraphRequest(
'/me',
null,
this._responseInfoCallback,
);
new GraphRequestManager().addRequest(infoRequest).start();
}
However, I can't find any further document. I have no idea what each parameters do.
The result for these codes above is this.
I also don't know how to get the result.
However, when I try to modify '\me' to 'me?fields=id,name', It failed.
Although I have asked for permission
<LoginButton
publishPermissions={["publish_actions,user_birthday, user_religion_politics, user_relationships, user_relationship_details, user_hometown, user_location, user_likes, user_education_history, user_work_history, user_website, user_managed_groups, user_events, user_photos, user_videos, user_friends, user_about_me, user_status, user_games_activity, user_tagged_places, user_posts, user_actions.video, user_actions.news, user_actions.books, user_actions.music, user_actions.fitness, public_profile, basic_info"]}
onLoginFinished={
(error, result) => {
if (error) {
alert("login has error: " + result.error);
} else if (result.isCancelled) {
alert("login is cancelled.");
} else {
AccessToken.getCurrentAccessToken().then(
(data) => {
meow_accesstoken = data.accessToken
alert(meow_accesstoken.toString())
}
)
}
}
}
onLogoutFinished={() => alert("logout.")}/>
But it does not print out what error, just object Object.
So, the problem is that I don't understand the sample code which Facebook provide with no explanation.
Here is my question that I really need you help me:
First at all, please check the javascript code that I currently looking at?
How to use graph API in react-native-fbsdk to retrieve some user information (example: full name) and successfully display it (use alert) ?
What each parameters in GraphRequest() do ?
What is the structure of error object and result object in _responseInfoCallback ?
SOLUTION
Thanks to #Samuel answer, I have updated my code
testRequestGraphAPI: function(){
const infoRequest = new GraphRequest(
'/me',
{
parameters: {
fields: {
string: 'email,name,first_name,middle_name,last_name' // what you want to get
},
access_token: {
string: meow_accesstoken.toString() // put your accessToken here
}
}
},
this._responseInfoCallback // make sure you define _responseInfoCallback in same class
);
new GraphRequestManager().addRequest(infoRequest).start();
}
And the callback
_responseInfoCallback: function(error: ?Object, result: ?Object) {
alert("meow response");
if (error) {
alert('Error fetching data: ' + error.toString());
console.log(Object.keys(error));// print all enumerable
console.log(error.errorMessage); // print error message
// error.toString() will not work correctly in this case
// so let use JSON.stringify()
meow_json = JSON.stringify(error); // error object => json
console.log(meow_json); // print JSON
} else {
alert('Success fetching data: ' + result.toString());
console.log(Object.keys(result));
meow_json = JSON.stringify(result); // result => JSON
console.log(meow_json); // print JSON
}
}
*Note: For console.log(), you need to use "Debug JS remotely" then open Chrome developer tools to see the log.
Unfortunately the react-native-fbsdk documentation is not updated and the examples do not work well.
I got the same problem and I solved it by try and error.
To solve your problem you'll need to change your GraphRequest adding params and fields to it like this:
<LoginButton
onLoginFinished={
(error, result) => {
if (error) {
alert("login has error: " + result.error);
} else if (result.isCancelled) {
alert("login is cancelled.");
} else {
AccessToken.getCurrentAccessToken().then(
(data) => {
let accessToken = data.accessToken
alert(accessToken.toString())
const responseInfoCallback = (error, result) => {
if (error) {
console.log(error)
alert('Error fetching data: ' + error.toString());
} else {
console.log(result)
alert('Success fetching data: ' + result.toString());
}
}
const infoRequest = new GraphRequest(
'/me',
{
accessToken: accessToken,
parameters: {
fields: {
string: 'email,name,first_name,middle_name,last_name'
}
}
},
responseInfoCallback
);
// Start the graph request.
new GraphRequestManager().addRequest(infoRequest).start()
}
)
}
}
}
onLogoutFinished={() => alert("logout.")}/>
You'll need to enable the Remote JS Debug to see the console.log() info.
https://facebook.github.io/react-native/docs/debugging.html
And probably you need to get some permissions to get more info than names and email so it's a good idea to look the Facebook Graph API Documentation: https://developers.facebook.com/docs/graph-api/overview/
Reference:
https://github.com/facebook/react-native-fbsdk/issues/105#issuecomment-206501550
Here is an example of a custom button if you want to make one :)
FbLoginButton() {
LoginManager
.logInWithReadPermissions(['public_profile'])
.then(function (result) {
if (result.isCancelled) {
alert('Login cancelled');
} else {
AccessToken
.getCurrentAccessToken()
.then((data) => {
let accessToken = data.accessToken
alert(accessToken.toString())
const responseInfoCallback = (error, result) => {
if (error) {
console.log(error)
alert('Error fetching data: ' + error.toString());
} else {
console.log(result)
alert('Success fetching data: ' + result.toString());
}
}
const infoRequest = new GraphRequest('/me', {
accessToken: accessToken,
parameters: {
fields: {
string: 'email,name,first_name,middle_name,last_name'
}
}
}, responseInfoCallback);
// Start the graph request.
new GraphRequestManager()
.addRequest(infoRequest)
.start()
})
}
}, function (error) {
alert('Login fail with error: ' + error);
});
}
Thank you #Samuel.
I finally succeed to get user information from Facebook login because of your help!
But I struggled to figure out how can I get username and email literally from the result object cause I am a newbie in React & Javascript.
P.S. result["name"] is the point because it is object!!
So I added some code to yours for other people like me.
If you don't like using your code, just tell me that.
<LoginButton
onLoginFinished={
(error, result) => {
if (error) {
alert("login has error: " + result.error);
} else if (result.isCancelled) {
alert("login is cancelled.");
} else {
AccessToken.getCurrentAccessToken().then(
(data) => {
let accessToken = data.accessToken
alert(accessToken.toString())
const responseInfoCallback = (error, result) => {
if (error) {
console.log(error)
alert('Error fetching data: ' + error.toString());
} else {
console.log(result)
// Here's my code
alert('Success fetching data: ' + result["name"].toString() +
", " + result["email"].toString());
/*
if(your DB already got this email or something unique) {
// SignIn()
}
// when your DB doesn't have this email
else {
// Do signUp() with this infomation and SignIn()
}
*/
}
}
const infoRequest = new GraphRequest(
'/me',
{
accessToken: accessToken,
parameters: {
fields: {
string: 'email,name,first_name,middle_name,last_name'
}
}
},
responseInfoCallback
);
// Start the graph request.
new GraphRequestManager().addRequest(infoRequest).start()
}
)
}
}
}
onLogoutFinished={() => alert("logout.")}/>
My code was not retriving the user email, if you are having the same problem, just put 'email' in parameter's logInWithPermission
Not Working
LoginManager.logInWithPermissions(['public_profile']).then(...)
Working
LoginManager.logInWithPermissions(['public_profile', 'email']).then(...)
All Function
loginWithFacebook = () => {
LoginManager.logInWithPermissions(['public_profile', 'email']).then(
login => {
if (login.isCancelled) {
console.log('Login Cancelado');
} else {
AccessToken.getCurrentAccessToken().then(
(data) => {
const accessToken = data.accessToken.toString()
this.getInfoFromToken(accessToken)
})
}
},
error => {
console.log('Erro no login ', console.error(error)
)
}
)
}
getInfoFromToken = token => {
const PROFILE_REQUEST_PARAMS = {
fields: {
string: 'id, name, first_name, last_name, birthday, email'
},
}
const profileRequest = new GraphRequest('/me', { token, parameters: PROFILE_REQUEST_PARAMS },
(error, result) => {
if (error) {
console.log('Login Info has an error:', error)
} else {
console.log(result)
}
},
)
new GraphRequestManager().addRequest(profileRequest).start()
}
try this
import { GraphRequest, GraphRequestManager } from 'react-native-fbsdk';
export const GetInfoUSer = () => {
return new Promise((resolve, reject) => {
const infoRequest = new GraphRequest('/me', null, ((error, result) => {
if (error) {
reject(error)
} else {
resolve(result)
}
}))
new GraphRequestManager().addRequest(infoRequest).start();
})
}
and then
onLoginConFacebook = () => {
LoginManager.logInWithReadPermissions(['public_profile']).then(result => {
if (result.isCancelled) {
console.log(':(')
} else {
AccessToken.getCurrentAccessToken().then((data) => {
let myAccessToken = data.accessToken.toString();
GetInfoUSer().then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
}
).catch(error => {
console.log(':(')
})
}
})
}

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

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.
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.
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");
});