Many to Many relationship Get by id is not working - node.js

I tried to create a user & pet model in Many to Many relationship using mongoDB and sailsJS, db creation and loading the data's are just fine i checked in mongoDB for dbs and collection it exists. when i try to get the user list and pet list its showing me the contents but when i try to GET a single pet by its iD under user by his iD i'm not getting result
It shows "Response does not contain any data."
I tried to get PET like this
"http://localhost:1337/user/54ffd28e9d9ee93c166c7500/pets/54ffd28e9d9ee93c166c7503"
I also tried to display the pet by its name instead of iD its also not working
my sails version is 0.11.0..
my OS is Win7 64 bit

here's a maybe useful thing to know, when requesting by id while using mongoDB.
to get some document inside of a collection, passing the id through an URL, it is useful to use the const { ObjectId } = require('mongodb').
you can for instance do as following:
// VS code might or might not write this for you
// but if you VIM, you have to write yourself
const { ObjectId } = require('mongodb')
fastify.get('/something/:id', async function (req, reply) {
const db = await this.mongo.db;
await db.collection('YourCollection', callbackFunc);
function callbackFunc(err, col) {
if (err) reply.send(err)
col.findOne({ "_id": new ObjectId(req.params.id) }, (err, result) =>
{
console.log(result)
})
}
})
Let me know if it helps, best wishes

Related

Perform check on record before performing update with Prisma

I'm creating the backend for a simple app which allows users to create, update, and delete products. Using Express as my framework, with Postgres as my DB and Prisma, which I'm new to, as my ORM. Users and products have a one-to-many relationship. Prisma's documentation states that when updating a record, you should use the update method - so to update the name of a product with a given ID, your code would look something like this:
export const updateProduct = async (req, res) => {
const [productID, newProductName, userID] = [req.params.id, req.body.name, res.locals.user.id];
const product = await prisma.product.update({
where: {
id: productID,
},
data: {
name: newProductName
}
});
res.status(200);
res.json(product);
};
However, there's a problem here - I'm not checking to see that the product with the provided ID belongs to the user that has sent the request to update it. I have the ID of the user who has sent the request in the variable userID, and each product in the DB has a field belongsToID which is set to the ID of the user that the product belongs to. I should theoretically therefore be able to modify my query to get the product with the specified ID and a matching belongsToID like so:
export const updateProduct = async (req, res) => {
const [productID, newProductName, userID] = [req.params.id, req.body.name, res.locals.user.id];
const product = await prisma.product.update({
where: {
id: productID,
belongsToID: userID
},
data: {
name: newProductName
}
});
res.status(200);
res.json(product);
};
That, however, does not work - I get the following error: Type '{ id: any; belongsToID: any; }' is not assignable to type 'ProductWhereUniqueInput'. Object literal may only specify known properties, and 'belongsToId' does not exist in type 'ProductWhereUniqueInput'.ts(2322).
It appears that when trying to do a 'findUnique', Prisma doesn't allow non-unique fields to be used in the query (even if the combination of both fields is unique, as is the case here). I do get that logically, my query doesn't make much sense - the ID alone is already enough to find a unique entry without the second field, so in that sense, the second field is totally redundant. Yet how else am I meant to check that the belongsToID is what it should be before updating the record? Is there somewhere else within the object passed to .update where I can provide a check to be performed on the retrieved record before performing the update?
I believe that creating an index would allow me to query for both fields at once - but why should I have to create an index when the ID (which is already indexed) alone is all I need to retrieve the record I need? What I really need is a way to perform a check on a retrieved record before performing the update when using Prisma.table_name.update(), not a way to query for something with a unique combination of fields.

How do i access another table using another tables data im accessing with Sequelize

Okay so, I'm displaying a friends table with Sequelize in Nodejs, Everything goes to plan but I run into a problem. For the friends table i store the user ids and then access the user data with those ids then display it onto ejs. I want to access another table using the current tables data.
Heres the code to access the Friends table, I made it a function so i can access it from ejs
let getFriends = async (id) => {
const project = await database.Friends.findAndCountAll({ where: { fid: id } });
return project.rows
}
you can access another table using include.
new code will be
let getFriends = async (id) => {
const project = await database.Friends.findAndCountAll({ where: { fid: id },
include:[{
model:model.User,as:'user'}]
});
return project;
}

Delete from mongoDB by id

I would like to delete an item from my database using the id I passed to it, not by the object ID. How do I do this?
I have done some reading on deleteOne but I'm not quite sure how to go about putting it into use.
Each movie has a button to delete a movie so I need the id of each movie to be passed into the remove function. New to MERN stack here.
{
"_id": "5fa55741aae528142e96c9e6",
"id": 531219,
"user": "5fa406cf937ce199eeb176a1",
"title": "Roald Dahl's The Witches",
"overview": "large string ... ",
"poster_path": "/betExZlgK0l7CZ9CsCBVcwO1OjL.jpg",
"vote_average": "7.1",
"__v": 0
},
My code so far
router.delete('/:id/delete', auth, async (req, res)=>{
const movie = await Movie.findByIdAndRemove(req.body.id)
if(!movie){
res.send('Movie not found')
} else{
movie.remove()
res.send('movie deleted')
}
})
case REMOVE_MOVIE:
return{...state, wishlist:state.wishlist.filter(movie => movie.id !== payload)}
export const removeMovie = (id) => async dispatch => {
try {
await axios.delete(`/wishlist/${id}/delete`)
dispatch({
type: REMOVE_MOVIE,
payload:id
})
} catch (error) {
console.log('unable to delete movie')
console.error(error)
}
}
Your problem is findByIdAndRemove() looks for the _id and you want to use other value to execute the query. So you can use findOneAndDelete() function from Mongoose.
There are also more functios to do the job as deleteOne or findAndDelete. But if you want to delete by id I recommend use findOneAndDelete because return the deleted document.
Then you have to pass the id to query like this:
var deleted = await model.findAndDelete({id: "myID"})
Note that id is not the same as _id.
And that's all...
From documentation, to know how functions works:
findOneAndDelete()
Deletes a single document based on the filter and sort criteria, returning the deleted document.
deleteOne
Removes a single document from a collection.
And also, if you are using mongoose and you want delete by _id you can use findByIdAndDelete() function instead of findByIdAndRemove().
According to documentation
This function differs slightly from Model.findOneAndRemove() in that findOneAndRemove() becomes a MongoDB findAndModify() command, as opposed to a findOneAndDelete() command. For most mongoose use cases, this distinction is purely pedantic. You should use findOneAndDelete() unless you have a good reason not to.
I was able to remedy this issue using the advice above, but I needed to capture and assign the value of the movie being clicked on to the id value passed into findOneAndDelete. I did this by passing in req.params.id. So it was findOneAndDelete({id: req.params.id}). Now everything works as expected. Thanks!

Nodejs & Mongo pagination random order

I am running an iOS app where I display a list of users that are currently online.
I have an API endpoint where I return 10 (or N) users randomly, so that you can keep scrolling and always see new users. Therefore I want to make sure I dont return a user that I already returned before.
I cannot use a cursor or a normal pagination as the users have to be returned randomly.
I tried 2 things, but I am sure there is a better way:
At first what I did was sending in the parameters of the request the IDs of the user that were already seen.
ex:
But if the user keeps scrolling and has gone through 200 profiles then the list is long and it doesnt look clean.
Then, in the database, I tried adding a field to each users "online_profiles_already_sent" where i would store an array of the IDs that were already sent to the user (I am using MongoDB)
I can't figure out how to do it in a better/cleaner way
EDIT:
I found a way to do it with MySQL, using RAND(seed)
but I can't figure out if there is a way to do the same thing with Mongo
PHP MySQL pagination with random ordering
Thank you :)
I think the only way that you will be able to guarentee that users see unique users every time is to store the list of users that have already been seen. Even in the RAND example that you linked to, there is a possibility of intersection with a previous user list because RAND won't necessarily exclude previously returned users.
Random Sampling
If you do want to go with random sampling, consider Random record from MongoDB which suggests using an an Aggregation and the $sample operator. The implementation would look something like this:
const {
MongoClient
} = require("mongodb");
const
DB_NAME = "weather",
COLLECTION_NAME = "readings",
MONGO_DOMAIN = "localhost",
MONGO_PORT = "32768",
MONGO_URL = `mongodb://${MONGO_DOMAIN}:${MONGO_PORT}`;
(async function () {
const client = await MongoClient.connect(MONGO_URL),
db = await client.db(DB_NAME),
collection = await db.collection(COLLECTION_NAME);
const randomDocs = await collection
.aggregate([{
$sample: {
size: 5
}
}])
.map(doc => {
return {
id: doc._id,
temperature: doc.main.temp
}
});
randomDocs.forEach(doc => console.log(`ID: ${doc.id} | Temperature: ${doc.temperature}`));
client.close();
}());
Cache of Previous Users
If you go with maintaining a list of previously viewed users, you could write an implementation using the $nin filter and store the _id of previously viewed users.
Here is an example using a weather database that I have returning entries 5 at a time until all have been printed:
const {
MongoClient
} = require("mongodb");
const
DB_NAME = "weather",
COLLECTION_NAME = "readings",
MONGO_DOMAIN = "localhost",
MONGO_PORT = "32768",
MONGO_URL = `mongodb://${MONGO_DOMAIN}:${MONGO_PORT}`;
(async function () {
const client = await MongoClient.connect(MONGO_URL),
db = await client.db(DB_NAME),
collection = await db.collection(COLLECTION_NAME);
let previousEntries = [], // Track ids of things we have seen
empty = false;
while (!empty) {
const findFilter = {};
if (previousEntries.length) {
findFilter._id = {
$nin: previousEntries
}
}
// Get items 5 at a time
const docs = await collection
.find(findFilter, {
limit: 5,
projection: {
main: 1
}
})
.map(doc => {
return {
id: doc._id,
temperature: doc.main.temp
}
})
.toArray();
// Keep track of already seen items
previousEntries = previousEntries.concat(docs.map(doc => doc.id));
// Are we still getting items?
console.log(docs.length);
empty = !docs.length;
// Print out the docs
docs.forEach(doc => console.log(`ID: ${doc.id} | Temperature: ${doc.temperature}`));
}
client.close();
}());
I have encountered the same issue and can suggest an alternate solution.
TL;DR: Grab all Object ID of the collections on first landing, randomized using NodeJS and used it later on.
Disadvantage: slow first landing if have million of records
Advantage: subsequent execution is probably quicker than the other solution
Let's get to the detail explain :)
For better explain, I will make the following assumption
Assumption:
Assume programming language used NodeJS
Solution works for other programming language as well
Assume you have 4 total objects in yor collections
Assume pagination limit is 2
Steps:
On first execution:
Grab all Object Ids
Note: I do have considered performance, this execution takes spit seconds for 10,000 size collections. If you are solving a million record issue then maybe used some form of partition logic first / used the other solution listed
db.getCollection('my_collection').find({}, {_id:1}).map(function(item){ return item._id; });
OR
db.getCollection('my_collection').find({}, {_id:1}).map(function(item){ return item._id.valueOf(); });
Result:
ObjectId("FirstObjectID"),
ObjectId("SecondObjectID"),
ObjectId("ThirdObjectID"),
ObjectId("ForthObjectID"),
Randomized the array retrive using NodeJS
Result:
ObjectId("ThirdObjectID"),
ObjectId("SecondObjectID"),
ObjectId("ForthObjectID"),
ObjectId("FirstObjectID"),
Stored this randomized array:
If this is a Server side script that randomized pagination for each user, consider storing in Cookie / Session
I suggest Cookie (with timeout expired linked to browser close) for scaling purpose
On each retrieval:
Retrieve the stored array
Grab the pagination item, (e.g. first 2 items)
Find the objects for those item using find $in
.
db.getCollection('my_collection')
.find({"_id" : {"$in" : [ObjectId("ThirdObjectID"), ObjectId("SecondObjectID")]}});
Using NodeJS, sort the retrieved object based on the retrived pagination item
There you go! A randomized MongoDB query for pagination :)

Limit posted fields for insert

I'm trying to limit the fields a user can post when inserting an object in mongodb. I know ho i can enforce fields to be filled but I can't seem to find how to people from inserting fields that I don't want.
This is the code I have now for inserting an item.
app.post("/obj", function (req, res) {
var newObj = req.body;
//TODO filter fields I don't want ?
if (!(newObj .id || newObj .type)) {
handleError(res, "Invalid input", "Must provide a id and type.", 400);
return;
}
db.collection(OBJ_COLLECTION).insertOne(newObj, function(err, doc) {
if (err) {
handleError(res, err.message, "Failed to create new object.");
} else {
res.status(201).json(doc.ops[0]);
}
});
});
There's likely JS native ways to do this, but I tend to use Lodash as my toolbox for most projects, and in that case what I normally do is setup a whitelist of allowed fields, and then extract only those from the posted values like so:
const _ = require('lodash');
app.post("/obj", function (req, res) {
var newObj = _.pick(req.body, ['id', 'type','allowedField1','allowedField2']);
This is pretty straightforward, and I usually also define the whitelist somewhere else for reuse (e.g. on the model or the like).
As a side note, I avoid using 'id' as a field that someone can post to for new objects, unless I really need to, to avoid confusion with the autogenerated _id field.
Also, you should really look into mongoose rather than using the straight mongodb driver, if you want to have more model-based control of your documents. Among other things, it will strip any fields off the object if they're not defined in the schema. I still use the _.pick() method when there are things that are defined in the schema, but I don't want people to change in a particular controller method.

Resources