Mongoose Routing Specific Query Output - json

Mongoose newb question. I'm trying to build a MEAN application that queries an existing db based on user entered parameters. The database is being populated with JSONs by an outside program.
So far, I have my router successfully showing all of the Mongo records when I use the below router:
// Pulls all JSONs
router.get('/jsons', function(req, res, next) {
Json.find(function(err, jsons){
if(err) { return next(err);}
res.json(jsons);
});
});
Now I am trying to create a separate route which can show a specific field within that database. (Where TestLocation = "New York")
// Pulls a JSON with City New York
router.get('/jsons/NewYork', function(req, res, next) {
var queryNYC = Json.where({TestLocation: "New York"});
queryNYC.findOne(function(err, jsons) {
if(err) { return next(err);}
res.json(jsons);
});
});
This is returning null to me, though the original route shows that one JSON record does indeed have a TestLocation of New York.
My schema looks like:
var mongoose = require('mongoose');
// Base schema for the query results table
var JsonSchema = new mongoose.Schema({
uploadID: String,
uploadDate: Date,
testDate: Date,
username: String,
type: String,
markers: String,
image: String,
TestLocation: String
},
{
collection: 'data'
}
);
mongoose.model('Json', JsonSchema);
So my question:
A) Am I setting this up correctly
B) Do I need my Mongoose schema to match what's in the DB exactly? My Mongoose schema has a field TestLocation which matches a TestLocation field in the MongoDB database, but I also have a lot of fields in the MongoDB database that aren't included in the schema. Do I need to include these to see this working?
Thanks!

What you're doing looks fine, the data in your database does not have to be the same as your data. Use the following:
Json.findOne({TestLocation: "New York"}, function(err, jsons) {
if(err) { return next(err);}
res.json(jsons);
});

Related

How to update a value in an object stored in a Redis

Im very new to Redis but it seems like somthing my program need to work faster.
I have build my whole database with mongoose/mongodbAtlas.
But is there a way to update one item in the object I got from the database and set in cache. I want to update a location in the setted redis key many times and only need to save the last updated location to the actual database.
So far I have some code to get 1 object from the database and store it in redis but I want to implement the updating part in this function as it is used for the PUT request to update a persons location every second
const updateLocation = async (req, res) => {
const { id} = req.params;
if (!redisClient.isOpen) {
await redisClient.connect()
console.log('connected')
}
const value = await redisClient.get(`person-${id}`)
if (value) {
res.json(value)
// Here I would like to update the documents location everytime
//this endpoint is called from frontend
} else {
const res = await Person.findById(id);
await redisClient.set(`person-${id}`, res);
console.log("from source data")
res.status(200).json(res);
}
};

Having multiple get routes on a NodeJS / Express REST API?

I'm creating a JS web app using NodeJS and Express (with SQL/MySQL for the database), pretty much directly implementing this API tutorial: https://www.bezkoder.com/node-js-rest-api-express-mysql/ (just replacing 'tutorials' with 'Employees').
I'm trying to write API functions to get all Employees with certain attributes (in the SQL table), for example all employees with lastName = "Garcia" or all employees with teamID = 43682, etc.
In my routes.js file I have this:
module.exports = app => {
const employees = require("../controllers/employee.controller.js");
const router = require("express").Router();
// Create a new Employee
router.post("/", employees.create);
// Retrieve all Employees
router.get("/", employees.findAll);
// Retrieve all Employees with lastName
router.get('/', employees.findLastName);
... a bunch more CRUD functions ...
app.use('/api/employees', router);
};
And this is the corresponding Controller function:
exports.findLastName = (req, res) => {
const lastName = req.query.lastName; // tried changing req.query.lastName to req.params.lastName
Employee.getLastName(lastName, (err, data) => {
if (err)
res.status(500).send({
message:
err.message || "Error occurred while retrieving by last name."
});
else {
console.log(`Employees with lastName ${lastName} were found!` );
res.send(data);
}
});
};
exports.findAll = (req, res) => {
const title = req.query.title;
Employee.getAll(title, (err, data) => {
if (err)
res.status(500).send({
message:
err.message || "Some error occurred while retrieving employees."
});
else {
console.log(`Employee with ${title} title was found!` );
res.send(data);
}
});
};
The findAll route/function (just copied from that tutorial) works by finding all Employees with a certain ID number (the primary key in the DB) and I know that works from testing it through Postman. I wrote the findLastName route/function by copying the findAll function and changing it to search by lastName, and making the corresponding functions in the model and controller classes.
The new function, findLastName, doesn't work... unless I put the route before the findAll route (or comment it out). Then it correctly calls all my functions and returns all employees with the lastName param.
What's actually happening here? Are you not allowed to have multiple .get() routes or something? If so, how would I implement a search API like this? This is my first time building a web app like this so admittedly I'm still a little hazy on how routing and all that works. Thank you for any help though!
In Express whenever the first route matches second will be ignored, so in your scenario you have two route.get with same path /
router.get('/', employees.findAll);
//Since route with path `/` is matched above already this will be ignored
router.get('/', properties.findLastName);
In order to find Employees with last name you will need to create a new route with param (param will contain the last name)
router.get('/:lastName', properties.findLastName);
You can access the param value like this req.params.lastName in controller

query DB for unknown number of params

Here is a simple express router, I want to give it query params and search the DB for them.
so, if the URL is like this api?x=value1 the app should query the DB for { x:value1 }
if the URL is api?x=value1&y=value2 the app should query the DB for { x:value1, y:value2 }
Since I don't know the number of params in advance, I have created an empty object and appended it with the params if existed.
if there are no params I want to retrieve all documents in DB.
.get(function (req, res){
let update_issue= {}; /*empty object*/
if(req.query.issue_title){update_issue["issue_title"] = req.query.issue_title}
if(req.query.issue_text){update_issue["issue_text"] = req.query.issue_text}
if(req.query.created_by){ update_issue["created_by"] = req.query.created_by }
/*append object if param exists*/
if(Object.keys(update_issue).length !== 0 ){ /*check if that object is not empty*/
db.collection('issues').find(update_issue, (err, data)=>{
res.json(data);
})
}
db.collection('issues').find().toArray((err, data)=>{
res.send(data);
})
this solution keeps giving me TypeError: Converting circular structure to JSON.
I understand that the object is in the form { x : "value" } and it should be JSON object like this { "x": "value" }
I tried flatted, JSON-stringify-safe still the same problem.
can you give me a solution to this problem, or an alternative way to continue the work.
I have solved the problem using node package called Api query params.
here is my code:
var aqp = require('api-query-params');
.get(function (req, res){
let update_issue= aqp(req.query);
if(Object.keys(update_issue).length !== 0 ){ /*check if that object is not empty*/
db.collection('issues').find(update_issue, (err, data)=>{
res.json(data);
})
}
db.collection('issues').find().toArray((err, data)=>{
res.send(data);
})
here is the package : https://www.npmjs.com/package/api-query-params

Getting auto-generated (via trigger) field from an insert in sequelize

I have a base controller for generic insert/update operations across the whole API, using only a table dictionary so we can use the same function to insert data into many tables.
The problem is there is a table that uses a correlative number generated via trigger, and when sequelize returns the inserted value, it includes the new ID but the correlative field returns empty, and I need it to show it on the interface.
I've thought of just querying the new field again to the API, or querying it on the same save function again when it includes these certain tables names, but is there a way to tell sequelize to "wait" for this new generated value and then return the data alright? Just like getting the new ID
Or maybe this needs to be fixed on the database? I don't have much experience in that field, but we are using MySQL if that helps.
function Init(models, dictionary) {
this.post = (req, res, next) => {
const { obj } = req.body;
const model = models[dictionary[obj._type]];
//Just stripping fields starting with "_"
const objClear = {};
for (const attr in obj) {
if (attr.charAt(0) !== '_') {
objClear[attr] = obj[attr];
}
}
//Saving
model.create(objClear).then(
(objSaved) => {
const data = {
obj: objSaved.get({ plain: true }),
action: 'inserted',
};
//I guess I could query the new row here again
res.json(data);
},
).catch(next);
};
}
module.exports = {
Init,
};
The response looks like:
{"obj":{"TOTAL":"0","ID":14,...,"TRANSACTION_NO":""},"action":"inserted"}
Where TRANSACTION_NO is the field generated with a trigger.
AFAIK, you have to query the new row unless you use Postgres (in which case you might try the Model.create option called "options.returning")
Two quick tests that did NOT solve the problem:
an afterCreate hook - the model still shows fields created by a trigger as null.
a model having a default value from a DB function - the model shows the function call,
not the result of the function (which does make it to the DB field).
Hope someone else has a solution!

mongoose return json list of tags specified as subdocuments

so i am having this problem that keeps me busy for the past 4 days, i am having a schema and subdocument schema like this:
var mongoose = require( 'mongoose' ),
Schema = mongoose.Schema;
var ProjectSchema = new Schema({
name: String,
author: String,
category: String,
description: String,
tags: [TagSchema]
});
var TagSchema = new Schema({
name: String,
date: Date
});
mongoose.model( 'TagSchema', TagSchema );
mongoose.model( 'Project', Project );
and what i want to have is a list of all tags of all ProjectSchemas, whatever i try i either get NONE or just the ones of the most current Project. i just dont know further because whatever i do i always end up failing on this one. what am i doing wrong, is there no such thing as a findAll for mongoose?
app.get('/tags.json', function(req, res) {
TagSchema.find({}, function ( err, tags ){
var json = JSON.stringify(tags);
console.log(json);
tags = tags.map(function (tag) {
return tag.toObject();
});
console.log(json == JSON.stringify(tags));
res.json(json);
});
});
and
app.get('/tags.json', function(req, res) {
Project.find(function (err, projects) {
projects.forEach( function( project ){
res.json(project.tags);
});
});
});
and anything else i tried just returned
[ ]
or errored out...
(additionally i wonder, how can i make sure that if i add a tag to a project and its already existant how i can keep it from adding.)
You are trying to call find on the schema, when you should be trying to call it on a model.
If you change the bottom of your file to:
var TagModel = mongoose.model( 'TagModel', TagSchema );
var ProjectModel = mongoose.model( 'ProjectModel', Project );
and then in your app.get function calls:
app.get('/tags.json', function(req, res) {
TagModel.find({}, function ( err, tags ){ //changed this to the model instead of the schema
var json = JSON.stringify(tags);
console.log(json);
tags = tags.map(function (tag) {
return tag.toObject();
});
console.log(json == JSON.stringify(tags));
res.json(json);
});
});
and
app.get('/tags.json', function(req, res) {
ProjectModel.find(function (err, projects) {
projects.forEach( function( project ){
res.json(project.tags);
});
});
});
Models are constructors compiled from your schema definitions and represent the documents that can be saved and queried from the db.
When you use TagSchema in its own model and embedded in ProjectSchema like you are, it's important to understand that the docs in the tags collection and the docs in the tags array of project docs have no inherent connection. So if you save tags as part of a project, those won't end up in the tags collection unless you explicitly add them to that as well.
A few specific problems in your posted code:
You need to define TagSchema before you use it in ProjectSchema.
You should be passing ProjectSchema into the mongoose.model call, not Project.
Keep your schema and model names separate as it's not clear what's what in your code.
with the help of #alawson421's code and some array magic it works perfectly, thanks again #JohnnyHK for showing me the difference between a schema and a model. heres the working fix:
app.get('/tags.json', function(req, res) {
ProjectModel.find(function (err, projects) {
var taglist = [];
projects.forEach( function( project ){
//console.log(JSON.stringify(project.tags));
project.tags.forEach( function( tag ){
console.log(tag);
taglist.push(tag.name);
});
});
res.json(taglist);
});
});
and the output is:
[
"test",
"meep",
"lalela",
"another test",
"ihopethisworks",
"noderocks",
"i am not a mongo",
"ice cream rocks"
]