Avoid looping over visited URLs generated by mongodb and express - javascript

I have a mongodb collection, in which each document is accessible through a specific url.
The goal is to display a random document url to the user, which has not been visited before, until the user has seen all documents in the collection, then the whole collection shall be 'cleared' so it is accessible again.
I have thought about using cookies to achieve it, but I haven't found a way to do it.
The application is built using express for nodejs, with the mongoose module for mongodb.
Model.class:
var mongoose = require('mongoose'),
URLSlugs = require('mongoose-url-slugs'),
AutoIncrement = require('mongoose-sequence')(mongoose),
Schema = mongoose.Schema;
var dilemmaSchema = new Schema({
dilemma_title: String,
red_dilemma: String,
blue_dilemma: String,
red_dilemma_votes: {
type: Number,
default: 0
},
blue_dilemma_votes: {
type: Number,
default: 0
}
});
dilemmaSchema.plugin(AutoIncrement, {
inc_field: 'id'
});
dilemmaSchema.plugin(URLSlugs('dilemma_title'));
module.exports = mongoose.model('Dilemma', dilemmaSchema);
Code snippet from Router:
dilemmaRouter.route('/next')
.get(function (req, res) {
Dilemma.count().exec(function (err, count) {
var random = Math.floor(Math.random() * count);
Dilemma.findOne().skip(random).exec(function (err, dilemma) { //This function is supposed to redirect to an unvisited URL, and mark it as visited
dilemmaID = dilemma._id;
res.redirect('/' + dilemma.id + '/' + dilemma.slug);
})
})
})
How the database entries are looked up
dilemmaRouter.route('/:id/:slug')
.get(function (req, res) {
const _id = req.params.id;
const _slug = req.params.slug;
let query = {
id: _id,
slug: _slug
}
Dilemma.findOne(query, function (err, dilemma) {
if (err) {
console.log(err);
} else {
if (dilemma === null) {
res.redirect('/');
} else {
res.render('index', {
dilemma: dilemma
})
}
}
})
})

If you cycle through documents with an order and then not random, you just can add +X to your current id.
If it's random, you need to store for all users all documents they already have seen. So you need another 'table' in your database, or you need to add a field inside your user model where your store all documents seen.
The 'best' solution is think right now would be to add this field in your user model (or be able to know that IP X already saw document A and B). When your user try to access your page, you get the list of all id for the document you have, remove the id saw by the user, and do a random inside this list.
dilemmaRouter.route('/next')
.get(function (req, res) {
Dilemma.count().exec(function (err, count) {
// find all documents
User.find({'idUserOrIP' : 'userIPorID'}).exec(function(user) {
var userListSaw = user.listSaw;
})
// create a list with all your document id
var allDocs = [1...100];
// remove id already seen (user saw id 1 to 3)
allDocs = [4...100];
// random now store the index of the id of the document you want to display
var random = Math.floor(Math.random() * allDocs.length);
// just find your document with the id you just get
Dilemma.find({'id' : allDocs[random]}).exec(function (err, dilemma) { //This function is supposed to redirect to an unvisited URL, and mark it as visited
dilemmaID = dilemma._id;
res.redirect('/' + dilemma.id + '/' + dilemma.slug);
})
})
})

Related

Model.create() from Mongoose doesn´t save the Documents in my Collection

I have created a sigle app with a Schema and a Model to create a Collection and insert some Documents.
I have my todoModel.js file:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const todoSchema = new Schema({
username: String,
todo: String,
isDone: Boolean,
hasAttachment: Boolean
});
const Todos = mongoose.model("Todo", todoSchema);
module.exports = Todos;
Then I have created a setUpController.js file with a sample of my Documents. Then I create a Model and I pass my sample of Documents and my Schema. I create a response to send tje result in JSON.
Everything good here, as I get the result in json when accessing to the route.
Here is the code:
Todos.create(sampleTodos, (err, results) => {
if (!err) {
console.log("setupTodos sample CREATED!")
res.send(results);
}
else {
console.log(`Could not create the setupTodos Database sample, err: ${err}`);
}
});
My problem is that this Documents don´t get saved in the collection !! When I access to the database, nothing is there.
This is my app.js file:
mongoose.connect("mongodb://localhost:27017/nodeTodo")
.then(connection => {
app.listen(port);
})
.catch(err => {
console.log(`Could not establish Connection with err: ${err}`);
});
Could anyone help me please ?
Thank you
Try creating an instance and making the respective function call of that instance. In your case, save the document after creating an instance and it works like a charm.
const newTodos = new Todos({
username: "username",
todo: "todos",
isDone: false,
hasAttachment: flase
});
const createdTodo = newTodos.save((err, todo) => {
if(err) {
throw(err);
}
else {
//do your staff
}
})
after the collection is created you can use the function inserMany to insert also a single document the function receives an array of objects and automatically saves it to the given collection
example:
Pet = new mongoose.model("pet",schemas.petSchema)
Pet.insetMany([
{
//your document
}])
it will save only one hardcoded document
I hope it was helpful

TypeError: Cannot set property 'user' of undefined<br> at [file path] sqlite3

I am receiving an undefined error when attempting to set a session for the user upon validation of credentials on login. I am trying to use express-session to create the session for the user but do not have it directly imported into the file (I was guided to not do so) but am unsure how to resolve this error given. Any help and insight would be much appreciated!
End point:
router.post("/login", async (req, res, next) => {
try {
const { username, password } = req.body
// * checks for record existence in db, assigns record to var for access
const user = await users_access.findByFilter({ username })
if (!user) {
return res.status(401).json({ message: 'invalid crededentials' });
}
// * compares the entered password to the hash on record
const passwordValid = await secure.compare(password, user.password)
// * handling invalid responses + creating session
if (!passwordValid) {
return res.status(401).json({ message: 'invalid credentials' });
}
req.session.user = user
res.json({ message: `welcome, ${user.username}!`})
} catch(error) {
next(error)
}
});
application model:
// * add users to the datbase
// * inserts argument into user table in db access
// * returns a user found by id
const add = async (user) => {
const [id] = await database_access("users")
.insert(user)
return findById(id)
}
// * find user record with username and password
const find = () => {
return database_access("users").select("id", "username")
}
// * find user by filter
const findByFilter = (filter) => {
return database_access("users")
.select("id", "username", "password")
.where(filter)
.first()
}
// * find user with id
const findById = (id) => {
return database_access("users")
.select("id", "username")
.where({ id }) // desctructuring id from the request
.first()
}
module.exports = {
add,
find,
findByFilter,
findById
}
if you need to see any additional code to assess I am happy to provide but believe this is the source of issue per the error response. Thank you in advanced!
so I guess you are using the express-session module in your entry file for the server, app.js, server.js, index.js however you call it.
this login handler require to be used in a context where the session is available.
I think what you want is not a unit test for this particular router, but an integration test, to test this router in the context of your app.
This is all I can see from the information you provided. If this was not helpful enough, maybe you can show us your servers main file. and how this router is used.

How do I return an array of documents using an array of users object ids in mongoose?

I am trying to create a simple back end blog api with user authentication and authorization. It is built with mongoose and express. In my userSchema, I have a property that is an array called "subscribedTo". Here, users can subscribe to different users to get their blogs. The subscribedTo array stores objectIDs of the users that wished to be subscribed too.
Here is my code:
router.get('/blogs', auth, async (req, res) => {
//auth middleware attaches user to the request obj
try {
let blogs = []
req.user.subscribedTo.forEach(async (id) => {
let ownersBlogs = await Blog.find({owner:id})
blogs = [...blogs, ...ownersBlogs]
console.log(blogs)//consoles desired output of users blogs
})
console.log(blogs)//runs first and returns []
res.send(blogs)
}catch(e){
res.status(500).send(e)
}
})
When I use postman for this route it returns [] which is understandable. I can't seem to res.send(blogs) even though the blogs variable returns correctly in the forEach function.
Is there a better way to do this?
You can use without loop like as bellow
Blog.find({ owner: { $in: req.user.subscribedTo } }, function (err, blogResult) {
if (err) {
response.send(err);
} else {
response.send(blogResult);
}
});
OR
send response after loop completed like as bellow
router.get('/blogs', auth, async (req, res) => {
//auth middleware attaches user to the request obj
try {
let blogs = []
let len = req.user.subscribedTo.length;
let i = 0;
if (len > 0) {
req.user.subscribedTo.forEach(async (id) => {
let ownersBlogs = await Blog.find({ owner: id })
blogs = [...blogs, ...ownersBlogs]
console.log(blogs)//consoles desired output of users blogs
i++;
if (i === len) {
//send response when loop reached at the end
res.send(blogs)
}
})
} else {
res.send(blogs);
}
} catch (e) {
res.status(500).send(e)
}
});
You can find all the documents without a foreach loop, use $in
Blog.find({owner:{$in:[array of ids]}});

How to use dynamic query for the rest api

I am beginner of javascript and I am trying to create a simple rest api using node.js. This is so far, I have it.
I have a database called testDb and table called testMeasurement in influxdb. testMeasurement table contains DateOfBirth,ID,FirstName,LastName
(ID is tag in my testMeasurement table)
var express = require('express');
const Influx = require('influx')
var app = express();
const influx = new Influx.InfluxDB('http://user:password#localhost:8086/testDb')
app.listen(3000, 'localhost');
app.get('/myapi', function (req, res) {
influx.query('select * from testMeasurement').then(result => {
res.json(result)
}).catch(err => {
res.status(500).send(err.stack)
})
})
Now, Above gives me all the data which I have in testMeasurement table from database "testDb".
How do I define my query in a dynamic way so that I can filter my result?
for eg. if I type localhost/myapi/ID={someValue}, this should give me the relatedData of that ID.
Any advice would be so helpful.
There are many ways to achieve what you want. The best way to do it is using wildcards. Example:
app.get('/myapi/:userId', (req, res) => {
var query_str = 'select * from testMeasurement';
if (req.params.userId){
query_str += ' where id = ' + req.params.userId;
}
influx.query(query_str).then(result => {
res.json(result)
}).catch(err => {
res.status(500).send(err.stack)
})
});
That implies that you must have a structured API to consume, having nodes for each item. If you just want to test a little bit, one basic example is to test for GET params like:
app.get('/myapi', function (req, res) {
var query_str = 'select * from testMeasurement';
if (req.query.id != null){
query_str += ' where id = ' + req.query.id;
}
influx.query(query_str).then(result => {
res.json(result)
}).catch(err => {
res.status(500).send(err.stack)
})
})
Hope it helps!

How does this callback call work in Node.js with mongo .find call

models/category.js
var mongoose = require('mongoose');
// Category Schema
var categorySchema = mongoose.Schema({
title: {
type: String
},
description: {
type: String
},
created_at: {
type: Date,
default: Date.now
}
});
var Category = module.exports = mongoose.model('Category', categorySchema);
// Get Categories
module.exports.getCategories = function(callback, limit) {
Category.find(callback).limit(limit).sort([['title', 'ascending']]);
}
routes/categories.js
var express = require('express');
var router = express.Router();
Category = require('../models/category.js');
router.get('/', function(req, res, next) {
Category.getCategories(function(err, categories) {
if (err) res.send(err);
res.render('categories',
{
title: 'Categories',
categories: categories
});
});
});
router.post('/add', function(req,res) {
res.send('Form Submitted');
});
module.exports = router;
I got a few questions about this code
a) how does the callback mechanism work from routes/categories.js when we pass that callback function to models/category.js in Category.find(callback). That seems bizarre to me since we are doing a whole res.render which becomes part of Category.find() ?
b) Where is limit specified?
c) Why isn't there var in front of Category = require('../models/category.js');
a) that is indeed what happens, and is good: res.render will not get called until the find() operation executes on the database and a result is sent back for the mongoose code to return to you. You want to run the callback function after you get the result for your query, and so calling res.render before would be much more bizarre.
b) in the documentation. http://mongoosejs.com/docs/api.html#model_Model.find yields a Query object, which may be synchronously (i.e. before the query is actually made to resolve at the database) further specified with where, limit, etc.
c) because someone got lazy. In this example it doesn't actually make a difference because without var (or const or let in modern JS) a variable declaration is tacked onto the local context, which in your file is the routes/categories.js module context, and because Categories is declared at the top of that scope, var doesn't change where the variable ends up being bound. But it's lazy, and for good example code, that should have var in front of it.

Categories

Resources