Many-many relation Sequelize with variant extra column - node.js

I have 3 table, with relation :
User.belongsToMany(Project, { through: Report});
Project.belongsToMany(User, { through: Report});
and my report table has data like :
{ user_id : 1, project_id : 1, percent: 20, date: 20/10/2022}
{ user_id : 1, project_id : 2, percent: 80, date: 20/10/2022}
{ user_id : 2, project_id : 1, percent: 90, date: 20/10/2022}
{ user_id : 1, project_id : 1, percent: 20, date: 21/10/2022}
...
What kind of this many-many relation ? And how can i query project table and get result format like below :
{
"project_id":1,
"users":[
{
"user_id":1,
"reports":[
{
"report_id":1,
"date":"20/10/2022",
"percent":20
},
{
"report_id":1,
"date":"21/10/2022",
"percent":20
}
]
},
....
]
}

Related

Filtering documents in mongodb and nodejs

Data in the database is stored as given below. if I do a query like
const food = await Nutrition.find()
Then I get this in response
[
{
_id: 6035ff4778b1893fa5e8080f,
name: 'apple',
weight: 100,
unit: 'gram',
carbohydrates: 14,
calories: 52,
proteins: 0.3,
fats: 0.2,
__v: 0
},
{
_id: 6036011437035541b0bd5e0a,
name: 'banana',
weight: 100,
unit: 'gram',
carbohydrates: 23,
calories: 89,
proteins: 11,
fats: 0.39,
__v: 0
},
{
_id: 6036011437035541b0bd5e0b,
name: 'melon',
weight: 100,
unit: 'gram',
carbohydrates: 45,
calories: 100,
proteins: 11,
fats: 0.39,
__v: 0
}
]
I have this controller in nodejs which fetch food nutrition from the database
const Nutrition = require('../model/nutritionalFacts')
exports.nutritionFacts = (async (req,res) =>{
try {
const food = await Nutrition.find()
console.log(food);
} catch (error) {
console.log('Error occurred',error.message);
}
})
Now in request (req), req.body is coming as
[
{ name: 'apple', id: 0, selected: true, weight: 100, unit: 'gram' },
{ name: 'banana', id: 1, selected: true, weight: 100, unit: 'gram' }
]
Now I want to filter only those documents from the database whose name matches with the name coming in an array of objects from the client as mentioned above without looping, just using MongoDB query syntax. Can we do this?
You can use $in operator to achieve that. You need to change your find method as below
var namesArr = ["banana", "melon"];
db.Nutrition.find({ "name" : { "$in": namesArr } })
Then the results for the sample above:
{
"_id" : ObjectId("60361058cce08c8b8ebe0509"),
"name" : "banana",
"weight" : 100,
"unit" : "gram",
"carbohydrates" : 23,
"calories" : 89,
"proteins" : 11,
"fats" : 0.39,
"__v" : 0
}
{
"_id" : ObjectId("60361058cce08c8b8ebe050a"),
"name" : "melon",
"weight" : 100,
"unit" : "gram",
"carbohydrates" : 45,
"calories" : 100,
"proteins" : 11,
"fats" : 0.39,
"__v" : 0
}
Try this
const Nutrition = require('../model/nutritionalFacts');
exports.nutritionFacts = (async (req, res) => {
try {
if (!Array.isArray(req.body.payload)) {
throw new Error("Invalid payload!") // or whatever error message u want!
}
const names = payload.map(item => item.name); // u cannot avoid this looping of payload!
const conditions = {
name: { $in: names }
};
const food = await Nutrition.find(conditions);
console.log(food);
} catch (error) {
console.log('Error occurred', error.message);
}
})

How to update multiple mongodb documents with different values based on a key?

I am trying to figure out a way to update multiple documents in a collection with different values based on a key.
persons collection:
{
_id: 1,
name: "Jackie Chan",
Country: Australia
},
{
_id: 2,
name: "Brad Pitt",
Country: Russia
},
{
_id: 3,
name: "Al Pacino",
Country: USA
}
Payload:
{
_id: 1,
name:"Jackie Chan",
Country:"China"
}
,{
_id: 2,
name:"Brad Pitt",
Country:"USA"
}
persons collection after update:
{
_id: 1,
name: "Jackie Chan",
Country: "China"
},
{
_id: 2,
name: "Brad Pitt",
Country: "USA"
},
{
_id: 3,
name: "Al Pacino",
Country: "USA"
}
SQL equivalent would be :
update t1.country = t2.country from persons t1 inner join #temptable t2 on t1._id=t2._id
None of the examples mentioned here explain how to do it. Unless I am missing something?
It seems like bulk write is exactly the right tool. Simply map the payload array so to make it an array of updates, such as:
db.persons.bulkWrite(payload.map( function(p) {
return { updateOne:{
filter: {_id: p._id},
update: {$set: {Country: p.Country}}
}}
}))
//code when run from mongodb client
> db.persons.find();
{ "_id" : 1, "name" : "Jackie Chan", "Country" : "China1" }
{ "_id" : 2, "name" : "Brad Pitt", "Country" : "Russia1" }
{ "_id" : 3, "name" : "Al Pacino", "Country" : "USA1" }
> var payload=[{_id: 1,name:"Jackie Chan",Country:"China2"},
... {_id: 2,name: "Brad Pitt",Country: "Russia2"},
... {_id: 3, name: "Al Pacino",Country: "USA2"}];
> print("payload: ",payload);
payload: [object Object],[object Object],[object Object]
>
> db.persons.bulkWrite(payload.map( function(p) {
... return { updateOne:{
... filter: {_id: p._id},
... update: {$set: {Country: p.Country}}
... }}
... }));
{
"acknowledged" : true,
"deletedCount" : 0,
"insertedCount" : 0,
"matchedCount" : 3,
"upsertedCount" : 0,
"insertedIds" : {
},
"upsertedIds" : {
}
}
> db.persons.find();
{ "_id" : 1, "name" : "Jackie Chan", "Country" : "China2" }
{ "_id" : 2, "name" : "Brad Pitt", "Country" : "Russia2" }
{ "_id" : 3, "name" : "Al Pacino", "Country" : "USA2" }
>

MongoDB get remaining seats of restaurant

I've problem to find remaining seats of particular restaurant in a particular Date range
Let me define my problem. Suppose I've a collection it called booking
In this have a following fields:
Booking ID
User ID
Restaurant ID
Booking Start Date (date saved in Timestamp)
Booking End Date (date saved in Timestamp)
Booked Seat
If there are 50 seats in restaurant. And I want to check available seats of a particular date range for example the date range is 6-12(This range in timestamp)"(Start Date) - (End Date)"
How to calculate the total remaining seats of restaurant.
If anyone have any idea please let me know.
Thanks in advance
Code is here
const where = {};
where.restaurant_id = 126;
where.user_id = 1;
where.start_date = { $gte: 6 };
where.end_date = { $gte: 12 };
const remainingSeats = getSeatsAvailability(where);
function getSeatsAvailability(where) {
return new Promise((resolve, reject) => {
bookingModel.find(where, (err, details) => {
if (err) { reject(err); } else { resolve(details); }
});
});
}
So, What I have Visualized is the below diagram, so there are basically four cases to be covered:
x y
a-----------|---------b |
a-|------------|---b
| a-------b |
| a-----|--------b
So the formulae are:
1. a<x, a<y | b>x, b<y
2. a<x, a<y | b>x, b>y
3. a>x, a<y | b>x, b>y
4. a>x, a<y | b<y, b>x
I am pasting an ROUGH example, take it as a hint to solve the problem at hand:
db.collection.aggregate([
{
$match: {
$or: [
{
a: {
$lte: 3,
$lte: 9
},
$or: [
{
b: {
$gte: 3,
$lte: 9
}
},
{
b: {
$gte: 3,
$gte: 9
}
}
]
},
{
a: {
$gte: 3,
$lte: 9
},
$or: [
{
b: {
$gte: 3,
$gte: 9
}
},
{
b: {
$gte: 3,
$lte: 9
}
}
]
}
],
r: 1
}
},
{
$group: {
_id: null,
sum_booked: {
$sum: "$c"
}
}
},
{
$project: {
seats_left: {
$subtract: [
50,
"$sum_booked"
]
}
}
}
])
The example dataset that I have tested on:
[
{
a: 1,
b: 10,
c: 10,
r: 1,
},
{
a: 2,
b: 5,
c: 15,
r: 1
},
{
a: 5,
b: 10,
c: 10,
r: 1
},
{
a: 7,
b: 15,
c: 15,
r: 12 <<<<========/////DIFF REST ID
}
]
Output:
[
{
"_id": null,
"seats_left": 15
}
]
db.booking.aggregate([{
$match: {
BookingStartDate: { $lte: date_range_end },
BookingEndDate: { $gte: date_range_start },
RestaurantID: restaurant_id
}
},
{
$group: {
_id: '$RestaurantID',
TotalBookedSeat: { $sum: '$BookedSeat' }
}
},
{
$project: {
LeftSeat: { $subtract: [50, '$TotalBookedSeat'] },
_id: 0
}
}
])
{ "_id" : ObjectId("5d8479f6902a52448029a6d0"), "bid" : 1, "uid" : 1, "rid" : 126, "bsd" : 1, "bed" : 10, "bs" : 10 }
{ "_id" : ObjectId("5d847a11902a52448029a6d1"), "bid" : 2, "uid" : 1, "rid" : 126, "bsd" : 2, "bed" : 5, "bs" : 15 }
{ "_id" : ObjectId("5d847a24902a52448029a6d2"), "bid" : 3, "uid" : 1, "rid" : 126, "bsd" : 5, "bed" : 10, "bs" : 10 }
{ "_id" : ObjectId("5d847a34902a52448029a6d3"), "bid" : 4, "uid" : 1, "rid" : 126, "bsd" : 7, "bed" : 15, "bs" : 15 }
db.booking.aggregate([{ $match: { bsd: { $lte: 12 }, bed: { $gte: 6 }, rid: 126 } }, { $group: { _id: '$rid', total_seat_sold: { $sum: '$bs' } } }])
The result of the above is 35, then 15 seats left.
This time it will works I think.

Can't create correct query to reach mongodb document

I need to translate a mongo shell command to the correct mongoose update in my express route.
The :id in the url is the ObjectId # in my document. The req.body will have an object with the key/values for title, season_number, episode_number, and watched. I thought I'd just replace this part of the mongo shell query
{ 'season_number': 1, 'episode_number': { $gt: 4, $lt: 6 },
with
{
'season_number': req.body.season_number,
'episode_number': {
$gt: req.body.episode_number - 1,
$lt: req.body.episode_number + 1
}
}
in the query but that did not find the document.
Route
router.put('/api/shows/:id/episodes/add', function(req, res){
var query = {
/*
I've tried many things but my query never returns the document
to update so I am pretty sure the problem is here
*/
}
var setObject = {
$push:{
'episodes':req.body
}
}
TV.update(query, setObject, function(err, results){
if(err){console.log(err)}
else{res.json(results)};
})
})
Mongo Shell Document
{
"_id" : ObjectId("581972b7b04acfc99b4dae0f"),
"title" : "Designated Survivor",
"poster" : "https://images-na.ssl-images-amazon.com/images/M/MV5BMTY5NzYzODU4N15BMl5BanBnXkFtZTgwNzA1MjUwMDI#._V1_.jpg",
"rated" : "TV-14",
"program_time" : 60,
"network" : "ABC",
"airs_on" : [
"Wednesday"
],
"streams_on" : [
"123Movies",
"Hulu Plus"
],
"genre" : [
"Drama"
],
"episodes" : [
{
"season_number" : 1,
"episode_number" : 1,
"title" : "Pilot",
"watched" : true
},
{
"season_number" : 1,
"episode_number" : 2,
"title" : "The First Day",
"watched" : true
},
{
"season_number" : 1,
"episode_number" : 3,
"title" : "The Confession",
"watched" : true
},
{
"season_number" : 1,
"episode_number" : 4,
"title" : "The Enemy",
"watched" : true
},
{
"season_number" : 1,
"episode_number" : 5,
"title" : "The Mission",
"watched" : true
},
{
"title" : "The Interrogation",
"season_number" : 1,
"episode_number" : 6,
"watched" : false
}
],
"test" : "gt four less than 6"
}
Mongo Shell Command that added the 6th Episode
db.tvShows.findOneAndUpdate(
{
$and: [
{ '_id': ObjectId('581972b7b04acfc99b4dae0f') },
{ 'episodes': {
$elemMatch: {
'season_number': 1,
'episode_number': { $gt: 4, $lt: 6 }
}
} }
]
},
{
$push: {
'episodes': {
'title': 'The Interrogation',
'season_number': 1,
'episode_number': 6,
watched: false
}
}
}
)

mongodb aggregation get the total number of matched document

I have a following sample docs saved in mongogb, like:
{
name: 'andy',
age: 19,
description: 'aaa aaa aaa'
}
{
name: 'andy',
age: 17,
description: 'bbb bbb bbb'
}
{
name: 'leo',
age: 10,
description: 'aaa aaa aaa'
}
{
name: 'andy',
age: 17,
description: 'ccc ccc ccc'
}
what the pipeline should look like to get the total number of name in each of matched sets? so I can use this sum number for next pipe. the pipeline I currently have is this:
var pip = [
{
$match: { name: 'andy' }
}
]
and I want to get this result like
{
name: 'andy',
age: 19,
description: 'aaa aaa aaa',
total_andy: 3
}
{
name: 'andy',
age: 17,
description: 'bbb bbb bbb',
total_andy: 3
}
{
name: 'andy',
age: 17,
description: 'ccc ccc ccc',
total_andy: 3
}
I am not exactly clear as to what you want. And i don't have enough reputation to ask for that in a comment. So let me have a shot at answering. If the answer isn't what you want, clarify the question further and we'll get to it...
var term1group = {$group :
{'_id' : '$name'},
'total_names' : {$sum : 1},
'ageAndDescription' : {$addToSet : {'$age', '$description'}}
}
var term2unwind = {$unwind : '$ageAndDescription'}
var term3project = {$project : {
_id : 0,
'name' : '_id',
'age' : '$ageAndDescription.age',
'description' : '$ageAndDescription.description',
'total_name' : 1
}
db.collection.aggregate(term1group, term2unwind, term3project);
Haven't tested but i am hopeful this will work.
You just need to use a $group and $sum to do a simple count. The output won't match exactly, but you could reformat it with NodeJS easily.
You apparently want to group on the three fields shown (name, age, and description). To do that, just add the fields and a field reference (using $):
{ $match: { name: 'andy' } },
{ $group: {
_id: { name: "$name", age: "$age", description: "$description"},
count: { $sum: 1}
}
}
To add the count of each group, include a $sum of 1 (for each document that matches the group).
Your output will look something like:
{ "_id" : { "name" : "andy", "age" : 17, "description" : "ccc ccc ccc" }, "count" : 1 }
{ "_id" : { "name" : "andy", "age" : 17, "description" : "bbb bbb bbb" }, "count" : 1 }
{ "_id" : { "name" : "andy", "age" : 19, "description" : "aaa aaa aaa" }, "count" : 3 }
If you used a projection with $project, you could also format the output to more closely match your original request:
{ $match: {name: 'andy' }},
{ $group: { _id: { name: "$name", age: "$age", description: "$description"} ,
count: {$sum: 1}}
},
{ $project : { name: "$_id.name", _id: 0, age: "$_id.age",
description: "$_id.description", total_andy: "$count"
}
}
Results:
{ "name" : "andy", "age" : 17, "description" : "ccc ccc ccc", "total_andy" : 1 }
{ "name" : "andy", "age" : 17, "description" : "bbb bbb bbb", "total_andy" : 1 }
{ "name" : "andy", "age" : 19, "description" : "aaa aaa aaa", "total_andy" : 3 }

Resources