Seqelize: Retrieve latest of hasMany relationship - javascript

Here is my relationship:
KbDevice.hasMany(KbDeviceEvent, { foreignKey: 'kb_device_id', as: 'events' })
Here is my query:
const allKbDevice = await KbDevice.findAll({
attributes: ['id', 'make', 'model'],
include: [
{
model: KbDeviceEvent,
as: 'events',
attributes: ['id', 'event', 'chamber_id','created_at'],
},
],
});
The issue is, instead of an array, I would like to get one KbDeviceEvent as the latest item based on created_at. I tried to use an include but it kept throwing an error. Can anyone help please?
Do you need to see the actual models to answer this question? If so, please comment.
Thank you,
EDIT:
Here is the result of the above query:
{
"id": "d75f9f96-a468-4c95-8098-a92411f8e73c",
"make": "Toyota",
"model": "Lexus",
"events": [
{
"id": "a737bf7b-2b2e-4817-b351-69db330cb5c7",
"event": "deposit",
"chamber_id": 2,
"created_at": "2022-09-01T22:36:19.849Z"
},
]
}
And I'm looking for:
{
"id": "d75f9f96-a468-4c95-8098-a92411f8e73c",
"make": "Toyota",
"model": "Lexus",
"event": { // I am the latest event
"id": "a737bf7b-2b2e-4817-b351-69db330cb5c7",
"event": "deposit",
"chamber_id": 2,
"created_at": "2022-09-01T22:36:19.849Z"
}
}

Query to fetch the associated model sorted by created_at:
const allKbDevice = await KbDevice.findAll({
attributes: ['id', 'make', 'model'],
include: [
{
model: KbDeviceEvent,
as: 'events',
attributes: ['id', 'event', 'chamber_id','created_at'],
},
],
order: [
[{ model: KbDeviceEvent, as: 'events' }, 'created_at', 'DESC']
],
limit: 1
});
Please note that this will always return the results in this manner:
{
"id": "d75f9f96-a468-4c95-8098-a92411f8e73c",
"make": "Toyota",
"model": "Lexus",
"events": [
{
"id": "a737bf7b-2b2e-4817-b351-69db330cb5c7",
"event": "deposit",
"chamber_id": 2,
"created_at": "2022-09-01T22:36:19.849Z"
},
]
}
since KbDevice model has hasMany relation with KbDeviceEvent model. You can do allKbDevice['events'][0] in your code to get the single event. If you are open to modifying the query, then you can achieve an output which doesn't have any array. However, device would be embedded inside event object. The query would be:
const kbDeviceEvent = await KbDeviceEvent.findAll({
attributes: ['id', 'event', 'chamber_id','created_at'],
order: [
['created_at', 'DESC']
],
limit: 1,
include: [
{
model: KbDevice,
as: 'device',
attributes: ['id', 'make', 'model']
},
]
});
This will output something like this:
{
"id": "a737bf7b-2b2e-4817-b351-69db330cb5c7",
"event": "deposit",
"chamber_id": 2,
"created_at": "2022-09-01T22:36:19.849Z"
"device": {
"id": "d75f9f96-a468-4c95-8098-a92411f8e73c",
"make": "Toyota",
"model": "Lexus",
}
}

Related

update nested array data in mongodb and node js

I want to update status in corder data. i try many time but i cannot update that data. if you know how update that data ??
{
"_id": "63064232cf92b07e37090e0a",
"sname": "Bombay collection ",
"name": "Hussain Khan",
"costomer": [
{
"cname": "Basan",
"cphone": 9863521480,
"_id": "632eeff2a9b88eb59d0210f0",
"corder": [
{
"clothType": "Shirt",
"date": "2022-10-21",
"status": "false",
"_id": "635283363edde6a0e9e92dc0"
},
]
}
]
}
You can use $[<identifier>]. For example:
db.collection.update(
{},
{$set: {"costomer.$[elemA].corder.$[elemB].status": newStatus}},
{arrayFilters: [
{"elemA._id": costomerId},
{"elemB._id": corderId}
]}
)
See how it works on the playground example

How To Group For Multiple Fields In Sequelize

I need one help related to the sequelize and I am using postgres.
I want to group records in sequelize. Also the form and user value comes after populate or using include method of sequelize.
I have applied this code but it didn't work:-
{
group: ['formId', 'userId', 'responseFrom'],
include: [ { model: forms, as: 'form' }, { model: users, as: 'user' } ]
}
Here the name of table is formAnswers.
[{
"id": 21,
"formId": 1,
"userId": 123,
"formQuestionId": 2,
"answer": "8,5,6",
"responseFrom": "WAITING",
"createdAt": "2020-01-14T02:31:19.173Z",
"form": {
"id": 1,
"name": "Choose Group",
},
"user": {
"id": 123,
"fullName": "Test User",
"username": "test123",
}
},
{
"id": 22,
"formId": 1,
"userId": 123,
"formQuestionId": 1,
"answer": "3",
"responseFrom": "WAITING",
"createdAt": "2020-01-14T02:31:19.178Z",
"form": {
"id": 1,
"name": "Choose Group",
},
"user": {
"id": 123,
"fullName": "Test User",
"username": "test123",
}
}]
This is the sample record, there will be multiple records for each user.
I want to group the records by using formId and userId. Also you can consider responseFrom in group by. I have tried with group in sequelize but its not working.
I need only single record which have formId and userId same. If we are using the above data so the expected output will be:-
[{
"id": 22,
"formId": 1,
"userId": 123,
"formQuestionId": 1,
"responseFrom": "WAITING",
"createdAt": "2020-01-14T02:31:19.178Z",
"form": {
"id": 1,
"name": "Choose Group",
},
"user": {
"id": 123,
"fullName": "Test User",
"username": "test123",
}
}]
I need to apply pagination for this as well so please keep it in mind.
Possible solution: (not tested)
formAnswers.findAll({
attributes: [
sequelize.literal(`DISTINCT ON("form_question_responses"."formId","form_question_responses"."userId","form_question_responses"."responseFrom") 1`),
'id',
'formId',
'userId',
'responseFrom',
],
include: [
{ model: forms, as: 'form', attributes: ['id', 'name'] },
{ model: users, as: 'user', attributes: ['id', 'fullName', 'username'] }
],
// automatically order by as used DISTINCT ON - DISTINCT ON strictly orders based on columns provided
// order: [
// ['formId', 'ASC'],
// ['userId', 'ASC'],
// ],
offset: undefined, // for no offset
limit: 50,
})

How to add name in JSONPATH

I have an array like this
{
"activities": [
{
"activityId": "60f672b20f38640008a6c29b",
"_id": "610cb887b149cb00095e1eb6",
"trackingProgress": [
{
"_id": "610cb8ee541fea0009189c36",
"dateRecorded": "2021-07-16T00:00:00.000Z",
"items": [
{
"_id": "610cb8ee541fea0009189c37",
"feelingType": "Satisfied",
"times": 2
}
]
},
{
"_id": "611358300fea490008d56ea7",
"dateRecorded": "2021-08-10T00:00:00.000Z",
"items": [
{
"_id": "6113583b0fea490008d56eae",
"feelingType": "Neutral",
"times": 1
}
]
},
{
"_id": "611b6068fed9100009a4f689",
"dateRecorded": "2021-08-17T00:00:00.000Z",
"items": [
{
"_id": "611b6068fed9100009a4f68a",
"feelingType": "Satisfied",
"times": 4
}
]
}
],
"title": "Losing & Managing Weight",
"description": "Losing weight",
"order": null,
"daysOfWeek": [
"tuesday"
],
"reminderTimeAt": "09:00 am",
"type": "Custom",
"progressOverview": {
"notSatisfied": 0,
"neutral": 1,
"satisfied": 6
}
}
]
}
I use JSONPATH to get _id in items
$.activities.[*].trackingProgress.[*].items[*].[_id]
it return an array like this
[
"610cb8ee541fea0009189c37",
"6113583b0fea490008d56eae",
"611b6068fed9100009a4f68a"
]
But, i want to keep the "_id", something like :
[
"_id":"610cb8ee541fea0009189c37",
"_id":"6113583b0fea490008d56eae",
"_id":"611b6068fed9100009a4f68a"
]
How can i do that?? Thank you a lots, i search internet but i found nothing
If you use map, you can get an array of items that each have an _id and value.
let result = [
"610cb8ee541fea0009189c37",
"6113583b0fea490008d56eae",
"611b6068fed9100009a4f68a"
].map(i => ({"_id": i}))
Results in:
[{ _id: "610cb8ee541fea0009189c37" },
{ _id: "6113583b0fea490008d56eae" },
{ _id: "611b6068fed9100009a4f68a" }]

MongoDB nested lookup with 3 levels and append new value to the result docs

I need to retrieve the entire single object hierarchy from the database as a JSON. Actually the proposal about any other solution to achive this result would be highly appriciated. I decided to use MongoDB with its $lookup support.
So I have four collections:
Users
{ "_id" : "2", "name" : "john" }
{ "_id" : "1", "name" : "Doe" }
Posts
{"_id": "2","body": "hello", likes: []},
{"_id": "1","name": "hello 4", likes: [1,]},
Comments
{"_id": "c2","body": "hello 3",postId: "1",likes: [1,2]},
{"_id": "c1","body": "hello 2",postId: "1",likes: [1,2]},
Replies
{"_id": "r1","name": "hello 4",commentId: "c1",likes: [1]},
{"_id": "r3","name": "hello five",commentId: "c2",likes: [1,2]}
I basically need to retrieve all posts with all corresponding comments and comments.replies as part of my result . My aggregation:
const posts = await PostModel.aggregate([
{
$lookup: {
from: "comments",
localField: "_id",
foreignField: "postId",
as: "comments",
},
},
{
$unwind: "$comments",
},
{
$lookup: {
from: "replies",
localField: "comments._id",
foreignField: "commentId",
as: "comments.replies",
},
},
]).sort({
createdAt: -1,
});
MongoDb PlayGround
The result is pretty weird. Some records are ok. But comments return an object . There are also some duplications on the post with _id="1".
[
{
"_id": "1",
"comments": {
"_id": "c2",
"body": "hello 3",
"likes": [
1,
2
],
"postId": "1",
"replies": [
{
"_id": "r3",
"commentId": "c2",
"likes": [
1,
2
],
"name": "hello five"
}
]
},
"likes": [
1
],
"name": "hello 4"
},
{
"_id": "1",
"comments": {
"_id": "c1",
"body": "hello 2",
"likes": [
1,
2
],
"postId": "1",
"replies": [
{
"_id": "r1",
"commentId": "c1",
"likes": [
1
],
"name": "hello 4"
}
]
},
"likes": [
1
],
"name": "hello 4"
}
]
Brief, This is my expected result.
I want to get all posts, with all comments and replies associated with them .
I want to append a count of likes likesCount:{$size:["likes"]} and since I have the user auth id(uid) ready I want to check if the user liked the post , comment or reply based on if isLiked: {$in:[ID(uid),"likes"]}
Since each post have multiple comments, after unwinding comments you need to group it together to form an array
Update
I have updated the fetch approach a lil bit like the below.
db.posts.aggregate([
{
$lookup: {
from: "comments",
localField: "_id",
foreignField: "postId",
as: "comments",
},
},
{
$unwind: "$comments",
},
{
$lookup: {
from: "replies",
localField: "comments._id",
foreignField: "commentId",
as: "replies",
},
},
{
$unwind: "$replies",
},
{
"$addFields": {
"replies.countOflikes": {
$size: {
$ifNull: [
"$replies.likes",
[]
]
}
},
"replies.isLiked": {
$cond: {
if: {
$eq: [
{
$size: {
$filter: {
input: "$replies.likes",
as: "item",
cond: {
$eq: [
"$$item",
1//id of the user whom you wanna check if liked the reply
]
}
}
}
},
0
]
},
then: false,
else: true
}
}
}
},
{
$group: {
_id: "$comments._id",
postId: {
$first: "$_id"
},
body: {
$first: "$body"
},
"comments": {
$first: "$comments"
},
replies: {
$push: "$replies"
}
}
},
{
$addFields: {
"comments.replies": "$replies"
}
},
{
$group: {
_id: "$postId",
body: {
$first: "$body"
},
comments: {
$push: "$comments"
}
}
}
])
Summary of the change
Unwinded both comments and it's replies
Added new fields for displaying isLiked and countOfLikes using addFields stage
grouped twice to reform original structure of the data(first grouped by comments then posts)
https://mongoplayground.net/p/lymCfeIIy9j

How to query two collections that have a "one-to-many" relationship in MongoDB?

We have two collections: "houses" and "events". Each document in the houses collection contains an "events" field with an array of id's that refer to events (a "one-to-many" relationship). The array can be empty.
"House" schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const HouseSchema = new Schema({
name: String,
district: String,
locality: String,
date: {
type: Date,
default: Date.now
},
events: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Event'
}]
});
module.exports = mongoose.model('House', HouseSchema);
"Event" schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const EventSchema = new Schema({
event: String,
details: String,
date: {
type: Date,
default: Date.now
},
house: {
type: mongoose.Schema.Types.ObjectId,
ref: 'House'
}
});
module.exports = mongoose.model('Event', EventSchema);
I need to query both "houses" and "events" and find those "houses" with referred "events" that meet all the criteria. I will give several options for queries.
Query with parameters only from "houses". Let's say there are two docs in the "houses" collection that satisfy the condition {"locality": "Facebook"}. The result should be like this:
[
{
"_id": "601bae8e26ed00591d571dbe",
"name": "Facebook testing department",
"district": "Los-Angeles",
"locality": "Facebook",
"events": [
{
"_id": "601bae8e26ed00591d571dbf",
"event": "Testing",
"details": "Testing software for production",
"date": "2020-07-31T21:00:00.000Z"
}
]
},
{
"_id": "601bae8e26ed00591d571dbc",
"name": "Facebook office",
"district": "Los-Angeles",
"locality": "Facebook",
"events": [
{
"_id": "601bae8e26ed00591d571dbd",
"event": "Conference",
"details": "Developers conference about 12345",
"date": "2020-07-31T21:00:00.000Z"
}
]
},
];
Query with parameters only for "events". Let's say there are two records in the "events" collection that satisfy the condition {"event": "Conference"}. The result is:
[
{
"_id": "601bae8e26ed00591d571dbc",
"name": "Facebook office",
"district": "Los-Angeles",
"locality": "Facebook",
"events": [
{
"_id": "601bae8e26ed00591d571dbd",
"event": "Conference",
"details": "Developers conference about 12345",
"date": "2020-07-31T21:00:00.000Z"
}
]
},
{
"_id": "601bae8e26ed00591d571dba",
"name": "Oxford",
"district": "London",
"locality": "Oxford",
"events": [
{
"_id": "601bae8e26ed00591d571dbb",
"event": "Conference",
"details": "About something",
"date": "2020-07-31T21:00:00.000Z"
}
]
},
];
Query with parameters for both "houses" and "events". If the criteria of our query is {"locality": "Facebook", "event": "Conference"}, then the result should be like this:
[
{
"_id": "601bae8e26ed00591d571dbc",
"name": "Facebook office",
"district": "Los-Angeles",
"locality": "Facebook",
"events": [
{
"_id": "601bae8e26ed00591d571dbd",
"event": "Conference",
"details": "Developers conference about 12345",
"date": "2020-07-31T21:00:00.000Z"
}
]
},
];
I have faced similar situations where it is necessary to make queries more complex than usual. But here we need to make a query - depending on the parameters - for one or two collections, and as a result, we should get "houses" with nested "events" that correspond the query parameters. I don't know MongoDB aggregation that well, would appreciate any help.
Declare your inputs
let locality = req.body.locality;
let event = req.body.event;
Initialize aggregation pipeline (replace HousesModel to your reql model name)
let p = HousesModel.aggregate();
Check condition for locality
if (locality && locality.trim()) p.match({ locality: locality });
$lookup with pipeline,
declare events array into let
match events condition in expression
check condition if events search is available then put condition
p.lookup({
from: "events", // replace your actual collection name if this is wrong
let: { events: "$events" },
pipeline: [
{
$match: {
$and: [
{ $expr: { $in: ["$_id", "$$events"] } },
(event && event.trim() ? { event: event } : {})
]
}
}
],
as: "events"
});
make sure events is not empty
p.match({ events: { $ne: [] } });
execute above pipeline
let result = await p.exec();
Playground
Combine above code in sequence and test.

Categories

Resources