Create Sequelize Query, Multiple Innerjoins and Wheres across multiple tables - javascript

I have created a successful mysql query that joins 2 tables that have foreign keys to each other. I got almost everything to work in Sequelize other than getting the user attached to the responses. My query does that successfully but I cannot understand how to get it.
This is my mysql query that works specifically gets the "users" that has the same id as the response "user_id"
SELECT *
FROM meetup_db.posts p
INNER JOIN meetup_db.responses r
INNER JOIN meetup_db.users u
WHERE p.id = r.post_id AND
r.user_id = u.id
This is my sequelize query that almost works but does not give me back the user in each response where user_id = id
const dbPostData = await Posts.findOne({
where: { id: req.params.id },
include: [
{ model: Users },
{
model: Responses,
include: {
model: Users,
where: { id: Responses.user_id }, //this is the line in question i need the user that has the same id as the Response.user_id
}
}
]
});
The sequelize correctlys outputs everything but the user, heres a sample output as you can see the response has Users listed inside but the users just show as [Object]
{
id: 1,
title: 'BBQ At My House!!',
description: 'Hey gang! Im having a BBQ at my house! I hope all can attend!!',
upvotes: 44,
location: '2113 Main St. Austin, TX.',
date_occuring: 2021-08-04T18:00:00.000Z,
created_at: 2021-08-06T04:42:01.000Z,
edited: false,
user_id: 1,
createdAt: 2021-08-06T04:42:01.000Z,
updatedAt: 2021-08-06T04:42:01.000Z,
User: {
id: 1,
username: 'Jennifer Wylan',
email: 'jwylan#gmail.com',
password: 'ewfchijwnsj',
image_url: '/assets/fake-pfp/fakeperson1.png'
},
Responses: [
{
id: 4,
response: 'Im in there like swimwear!',
user_id: 4,
post_id: 1,
createdAt: 2021-08-06T04:42:01.000Z,
updatedAt: 2021-08-06T04:42:01.000Z,
User: [Object] //these lines right here need to look like the above User: {} Object
},
{
id: 5,
response: 'weeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
user_id: 1,
post_id: 1,
createdAt: 2021-08-06T04:42:01.000Z,
updatedAt: 2021-08-06T04:42:01.000Z,
User: [Object] //these lines right here need to look like the above User: {} Object
}
],
}

I figured it out. So it worked already.
In my terminal the output of responses was showing the User key as [Object]
... Turns out when its like 3 tables deep the terminal just shows [Object] but it was actually there already, you also dont need the where: because it already looks for the foreign key

Related

find element in array within array mongoose

I have this data:
[
{
contacts: [ [Object] ],
_id: 614a1301a926d73628d791fe,
username: 'erick',
socketId: 'OB8uqmHnsGPKC3gcAAAB'
},
{
contacts: [
contacts: [ [Object] ],
_id: 614a1301a926d73628d791fe,
username: 'erick',
socketId: 'OB8uqmHnsGPKC3gcAAAB'
],
_id: 614a1306a926d73628d79204,
username: 'tachancka',
socketId: 'cN333_zn8r48K_0tAAAB'
}
]
And i need to find the username of 'erick' inside 'tachancka' contacts.
I have tried a few ways to do it but none of them worked for me, returning null.
For example:
User.findOne({contacts: {$elemMatch:{'username': 'erick'}}})
Or:
User.findOne({'contacts.username': 'erick'})
pls refer to sample here: https://mongoplayground.net/p/Np_MdrubPgT
you may use
db.User.find({
username: "tachancka",
"contacts.username": "erick"
})
When you have array of objects and if you want to match all the field within object then we use $elemMatch, here in your case i feel username is not part of contacts array hence you will not use $elemMatch

How do I add a new Subdocument if it's not exist already, then push another subdocument under it

I have a Schema named user on mongoose, and that schema has lastExams property as below:
lastExams: [{
lecture: {
type: String,
required: true
},
exams: [{
examID: {type: [mongoose.Schema.Types.ObjectId], ref: Exam, required: true},
resultID: {type: [mongoose.Schema.Types.ObjectId], ref: Result, required: true},
date: {type: Date, required: true},
result: {}
}]}]
With this, I want to keep the last 10 exams user have taken for each lecture they have. So after each exam, I want to check if the corresponding 'lastExams.lecture' subdocument already exists, if so, push the result that lastExams.$.exams array. Else, upsert that subdocument with first element of the exams array.
for example, thats an user document without any exams on it;
user: {
name: { firstName: '***', lastName: '***' },
email: '****#****.***',
photo: 'https://****.jpg',
role: 0,
status: true,
program: {
_id: 6017b829c878b5bf117dfb92,
dID: '***',
eID: '****',
pID: '****',
programName: '****',
__v: 0
},
lectures: [
{
some data
}
],
currentExams: [
some data
],
lastExams: []
}}
If user sends an exam data for math-1 lecture, since there is no exam with that lecture name, I need to upsert that document to get user document to become as below;
user: {
name: {
firstName: '***',
lastName: '***'
},
email: '****#****.***',
photo: 'https://****.jpg',
role: 0,
status: true,
program: {
_id: 6017 b829c878b5bf117dfb92,
dID: '***',
eID: '****',
pID: '****',
programName: '****',
__v: 0
},
lectures: [{
some data
}],
currentExams: [
some data
],
lastExams: [{
lecture: 'math-1',
exams: [
examID: 601 ba71e62c3d45a4f10f080,
resultID: '602c09b2148214693694b16c',
date: 2021 - 02 - 16 T18: 06: 42.559 Z,
result: {
corrects: 11,
wrongs: 9,
empties: 0,
net: 8.75,
score: 43.75,
qLite: [
'some question objects'
]
}
]
}]
}
}
I can do that like this;
User.findOneAndUpdate({email: result.user}, {$addToSet: {'lastExams': {
lecture: result.lecture,
exams: [{
examID: doc.examID, // btw, idk why, these id's saving to database as arrays
resultID: doc.id,
date: doc.createdAt,
result: doc.results
}]
}}})
But since this adds new subdoc with same lecture value each time. I am having to check if there is a subdoc with that lecture value first manually. if not so, run the above code, else, to push just exam data to that lectures subdoc, I am using below code;
User.findOneAndUpdate({email: result.user, 'lastExams.lecture': result.lecture }, {$addToSet: {'lastExams.$.exams': {
examID: doc.examID,
resultID: doc.id,
date: doc.createdAt,
result: doc.results
}}})
So, I am having to make User.find() query first to see if that lecture is already there, and pop an item if it's exams.lengt is 10. then deciding to what kind of User.findOneAndUpdate() to use.
Do you think there is any way to make this proccess in a single query? Without going to database 2-3 times for each exam save?
I know it's too long, but i couldn't put it straight with my poor english. Sorry.
Two methods:
Like you already did, multiple queries.
In the first query, you have to check if the subdocument exists, if it does, update it in the second query, else create subdocument with first item in the second query.
Using $out in an aggregation pipeline
If multiple round trips is only the issue, checkout $out aggregation pipeline method, which allows to write aggregation output to a collection. In there, you can first match you document, check if the subdocument exists, using $filter followed by $cond. Once you have the data ready, use $out to write it back to the collection.
NB: Aggregation pipeline is expensive operation than findOneAndUpdate IMO, so make sure you test the average latency for both 2 query method and single aggregation method and decide which is faster in your case.
PS: Sorry for not providing an example, I simply don't know it very well to create a working example for you. You can refer to the mongoDB docs for details.
https://docs.mongodb.com/manual/reference/operator/aggregation/out/
Also, there is a Jira ticket discussion going on for this specific use case in MongoDB. Hoping some simple solution will be implemented in MongoDB in the upcoming versions

How can i find and filter the blogs that belong to a user?

These are the blog objects in the Database:
[
{
_id: 5fec92292bbb2c32acc0093c,
title: 'Boxing ring',
author: 'T. Wally',
content: 'boxing stuff',
likes: 0,
user: {
_id: 5fd90181d1e88a13109433f9,
username: 'Johnny_23',
name: 'John Q'
},
__v: 0
},
{
_id: 5fec9481ce9c4a47a0ca6a2a,
title: 'Football OP',
author: 'RQ',
content: 'it`s football',
likes: 2,
user: {
_id: 5fec942dce9c4a47a0ca6a29,
username: 'malcomm',
name: 'Malcom'
},
__v: 0
}
]
And i want to only find and show the blogs that belong to a certain user
listRouter.get('/', async (request, response) => {
const blogs = await Blog
.find({}).populate('user', { username: 1, name: 1 })
response.json(blogs)
})
how can i access to the user id of every object and use it in a way that i can match the blogs?
Since property user has _id, you can construct your query like this:
const blogs = await Blog.find({ user: { _id: '_id of a certain user' } })
There will be a case where field user is a reference (its value is an ObjectId). In this case, try
const blogs = await Blog.find({ user: '_id of a certain user' }).populate('user', { username: 1, name: 1 })
When you are posting a blog you are taking the user id as filed in data. You can also send the blog id to user collection so that it will be easy to populate data from user collection.
//push blogid to user blogsId filed
await UserModel.updateOne({ _id: req.body.userId }, {
$push: {
blogsId: blog._id
}
})
You have to take a field in schema blogsId or whatever you want as an array field.

Sails Js populate don't retrieve all attributes

I have a problem with populate. I made a query to get User, Project and Topic information (Those are my 3 models). I need to show multiples dates in profile view. This is my code:
Project.js:
module.exports = {
attributes: {
name: {
type: "string"
},
topics: {
collection: "topic",
via: "projects"
},
members: {
collection: "user",
via: "projects"
},
content: {
collection: "content",
via: "projectData"
}
}
};
Topic.js:
module.exports = {
attributes: {
name: {
type: "string"
},
projects: {
collection: "project",
via: "topics"
}
}
};
in User.js:
show: function(req, res, next) {
User.findOne({id: req.session.User.id}).populateAll().exec(function prjFound(err, user){
if (err) return next(err);
if (!user) return next();
console.log(user);
res.view({
user: user
});
});
},
Console print this:
{ projects:
[ { name: 'Fran',
createdAt: '2017-06-19T21:33:17.152Z',
updatedAt: '2017-06-19T21:33:17.190Z',
id: 97 },
{ name: 'River Plate',
createdAt: '2017-06-19T21:36:38.757Z',
updatedAt: '2017-06-19T21:36:38.798Z',
id: 98 },
{ name: 'Guido',
createdAt: '2017-06-20T01:33:53.843Z',
updatedAt: '2017-06-20T01:33:53.926Z',
id: 99 } ],
group: [],
mat: 222222,
name: 'Francisco',
lastname: 'P',
email: 'fran#1.com.ar',
encryptedPassword: '$2a$10$nKp/eAOCDPw4BS.PvQCThe42wa2/8ZABw4JzA0no9GPVT4VjFl3ZO',
createdAt: '2017-06-19T21:32:10.535Z',
updatedAt: '2017-06-19T21:32:10.535Z',
id: '594842da6aeecd880ebab4e6'
}
I want to get all atributes of project model (Content, topic, and members), not only the name and id.
Anyone can explain Why my code is wrong?
Sails/Waterline populate/populateAll do 1 level of population. For 2 or deeper level you need to write code for it.
E.g. Gather ids of user's project and do populateAll on Project.find
Sailsjs doesn't currently support population within a populated field. Write a query in the returned response and append it to the field that you want to populate, send the response with your desired results.
Check this.
let result = await model.find(filter).populate("fieldName", {select:['attribute1','attribute1']})

How can I populate a nested related collection with SailsJS?

I have 3 collections,
Users,
Clan, which has many Clanmembers
Clanmembers, which has many Users
So when I do
Clan.find().populate('clanmembers');
It works fine and returns the result but then I get something like:
{ clanmembers:
[ { role: 'Entry',
owner: 2,
member: 2,
id: 1,
createdAt: null,
updatedAt: null } ],
owner:
{ username: 'tester',
email: 'tester',
about: 'This is\n\na test hello\n\n\n\n\n\n\n\n\nhiiii',
profileurl: '',
avatar: 'asd.jpg',
rank: '1',
id: 2,
createdAt: '2015-10-25T10:15:20.000Z',
updatedAt: '2015-10-25T21:31:47.000Z' },
clanname: 'testset',
clantag: 'testin',
id: 2,
createdAt: '2015-10-26T01:07:02.000Z',
updatedAt: '2015-10-26T01:07:02.000Z' }
What I'd like is also to be able to populate the clanmembers array's owners so that I can get usernames and what not of the clanmembers, how can I populate this nested array? Or is it not possible and I'll have to loop/find each one by their ID. Any information would be great thank you.
This can't be done as of now, although it has been in the talks for quite some time.
As you mentioned, you'll have to do the population manually. But you can avoid making separate calls for each subdocument.
What I usually do is collect the required ids in an array and run a single query to the database, then maintain a map of ids to documents which makes it pretty convenient to use in multiple places.

Categories

Resources