How do return the count of a particular field in psql db - postgresql

I have a database tables with the relationships as below.
Cagetory ---> CategorItems //One to many(CategoryItem has categoryId colum)
CatgeoryItems ---> Post // One to many(Post has categoryItemId).
I want to count the total number of records in Post groupBy the categoryItemId also returning along the categoryItem name, and the categoryName. I want columns like returned
CategoryName, item, totalInPostTable
I am using sequelize rdb mapper. When i use the query below
models.Post.findAll({
include: [{
model: models.CategoryItem,
attributes: ['item'],
required: true,
include: [{
model: models.Category,
attributes: ['name'],
required: true,
group: ["name"]
}]
}],
attributes: ['categoryItemId'],
})
It returns the record below
{
"categoryItemId": 1,
"CategoryItem": {
"item": "xxxxxx",
"Category": {
"name": "xxxxxx"
}
}
},
{
"categoryItemId": 1,
"CategoryItem": {
"item": "xxxxxx",
"Category": {
"name": "xxxxxx"
}
}
},
{
"categoryItemId": 1,
"CategoryItem": {
"item": "xxxxxx",
"Category": {
"name": "xxxxxx"
}
}
},
{
"categoryItemId": 1,
"CategoryItem": {
"item": "xxxxxx",
"Category": {
"name": "xxxxxx"
}
}
},
{
"categoryItemId": 1,
"CategoryItem": {
"item": "xxxxxx",
"Category": {
"name": "xxxxxx"
}
}
},
{
"categoryItemId": 2,
"CategoryItem": {
"item": "xxxxxx",
"Category": {
"name": "xxxxxx"
}
}
},
{
"categoryItemId": 4,
"CategoryItem": {
"item": "xxxxxx",
"Category": {
"name": "xxxxxx"
}
}
}
However,it failed and return null without error as soon as i groupBy the categpryItem id like below.
models.Post.findAll({
include: [{
model: models.CategoryItem,
attributes: ['item', [sequelize.fn('COUNT', sequelize.col('id')), 'no_category']],
required: true,
include: [{
model: models.Category,
attributes: ['name'],
required: true,
}]
}],
attributes: ['categoryItemId'],
group: ["categoryItemId"]
})
Pls what am i doing wrong ? ANy help would be appreciated.

Related

How to order a result in order to a nested include?

I'm using sequelizejs to generate a report with these models:
class Employees extends Model {
static associate(models) {
this.belongsToMany(models.Departaments, {
through: models.DepartamentsEmployees
});
this.belongsToMany(models.CostCenters, {
through: models.CentersEmployees
})
}
}
class CostCenters extends Model {
static associate(models) {
this.belongsToMany(models.Employees, {
through: models.CentersEmployees
});
}
}
class Departaments extends Model {
static associate(models) {
this.belongsToMany(models.Employees, {
through: models.DepartamentsEmployees
});
}
}
The question is that in the report I have to order the information with this order CostCenter->Departments->Employees, for example, If I have this information:
{
"id": 3,
"name": "Peru",
"employeesCount": "2",
"Employees": [
{
"id": "5801c217-4db8-40d0-aace-c936f3fa7c0d",
"name": "Julian",
"email": "augusto#gmail.com",
"Departaments": [
{
"id": 2,
"name": "Gerencia"
}
]
},
{
"id": "7a24a8d9-77b8-4bd3-a6d7-0053ca3d6357",
"name": "Hernesto",
"email": "mango#gmail.com",
"Departaments": [
{
"id": 1,
"name": "Marketing"
}
]
},
{
"id": "5b24a6f0-75b8-4bd3-p6d7-0053ca3c6357",
"name": "Maria",
"email": "maria#gmail.com",
"Departaments": [
{
"id": 1,
"name": "Marketing"
}
]
}
]
}
The information should have to be ordered like this:
{
"id": 3,
"name": "Peru",
"employeesCount": "2",
"Departaments": [
{
"id": 2,
"name": "Gerencia",
"Employees": [
{
"id": "5801c217-4db8-40d0-aace-c936f3fa7c0d",
"name": "Julian",
"email": "augusto#gmail.com"
}
]
},
{
"id": 1,
"name": "Marketing",
"Employees": [
{
"id": "5b24a6f0-75b8-4bd3-p6d7-0053ca3c6357",
"name": "Maria",
"email": "maria#gmail.com"
},
{
"id": "7a24a8d9-77b8-4bd3-a6d7-0053ca3d6357",
"name": "Hernesto",
"email": "mango#gmail.com"
}
]
}
]
}
I'm running the query with this:
return await models.CostCenters.findAll({
include: [
{
model: models.Employees,
through: {attributes: []},
include: [{
model: models.Departaments,
through: {attributes: []}
}]
}
],
attributes: [
'id',
'name',
[Sequelize.literal('COUNT("Employees"."id") OVER (PARTITION BY "CostCenters"."id")'), 'employeesCount']
]
});

How can I merge two documents, get rid of duplicates and keep certain data?

I have the following data, which describes who is going to do what work.
Basically I want to replace the "workId" and "userId" with objects that contain all the data from their respective documents and retain the "when" data.
I am starting with this data:
{
"schedule": {
"WorkId": "4e51dc1069c27c015ede4e3e",
"daily": [
{
"when": 1,
"U_W": [
{
"workId": "3a60dc1069c27c015ede1111",
"userId": "5f60c3b7f93d8e00a1cdf414"
},
{
"workId": "3a60dc1069c27c015ede1122",
"userId": "5f60c3b7f93d8e00a1cdf415"
}
]
}
]
}
}
Here is the user table
"userSchema": [
{
_id: "5f60c3b7f93d8e00a1cdf414",
Name: "Bob"
},
{
_id: "5f60c3b7f93d8e00a1cdf415",
Name: "Joe"
}
],
Here is the work table
"workSchema": [
{
_id: "3a60dc1069c27c015ede1111",
Name: "shovel"
},
{
_id: "3a60dc1069c27c015ede1122",
Name: "hammer"
}
]
what I want to end up with is this
{
"schedule": {
"WorkId": "4e51dc1069c27c015ede4e3e",
"daily": [
{
"when": 1,
"U_W": [
{
"work": {
"id": "3a60dc1069c27c015ede1111",
"name": "shovel"
},
"user": {
"id": "5f60c3b7f93d8e00a1cdf414",
"name": "bob"
}
},
{
"work": {
"id": "3a60dc1069c27c015ede1122",
"name": "hammer"
},
"user": {
"id": "5f60c3b7f93d8e00a1cdf415",
"name": "joe"
}
}
]
}
]
}
}
Here is my first attempt:
I have it joining the the two documents
How can I get rid of the duplicates ( bob:hammer and joe:shovel ) ?
and how do I include the "when" ?
Here is the playground that provides the following :
[
{
"_id": ObjectId("5a934e000102030405000000"),
"user_info": {
"Name": "Bob",
"_id": "5f60c3b7f93d8e00a1cdf414"
},
"work_role": {
"Name": "shovel",
"_id": "3a60dc1069c27c015ede1111"
}
},
{
"_id": ObjectId("5a934e000102030405000000"),
"user_info": {
"Name": "Bob",
"_id": "5f60c3b7f93d8e00a1cdf414"
},
"work_role": {
"Name": "hammer",
"_id": "3a60dc1069c27c015ede1122"
}
},
{
"_id": ObjectId("5a934e000102030405000000"),
"user_info": {
"Name": "Joe",
"_id": "5f60c3b7f93d8e00a1cdf415"
},
"work_role": {
"Name": "shovel",
"_id": "3a60dc1069c27c015ede1111"
}
},
{
"_id": ObjectId("5a934e000102030405000000"),
"user_info": {
"Name": "Joe",
"_id": "5f60c3b7f93d8e00a1cdf415"
},
"work_role": {
"Name": "hammer",
"_id": "3a60dc1069c27c015ede1122"
}
}
]
After beating my head against the wall for some time...
I found a pretty cool feature of mongo "references"
eg:
REF_work: { type: Schema.Types.ObjectId, required: true, ref: 'work' },
REF_person: { type: Schema.Types.ObjectId, required: true, ref: 'users' },
then when I call it from my get function I add a populate to the find
assignments.find(query).populate('daily.cp.REF_person').populate('daily.cp.REF_work');
I get exactly what I want:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"REF_person": {
"Name": "Bob",
"_id": "5f60c3b7f93d8e00a1cdf414"
},
"REF_work": {
"Name": "shovel",
"_id": "3a60dc1069c27c015ede1111"
}
},
{
"_id": ObjectId("5a934e000102030405000000"),
"REF_person": {
"Name": "Joe",
"_id": "5f60c3b7f93d8e00a1cdf415"
},
"REF_work": {
"Name": "hammer",
"_id": "3a60dc1069c27c015ede1122"
}
}
]

Mongoose schema set timestamp on nested document

I have a schema that looks as follows:
{
"_id": "5073c76a23ce3abf0f000001",
"asker": {
"userId": "fooId",
"firstName": "foo",
"lastName": "bar",
"points": "10",
"aboutMe": "Something about me"
},
"isBounty": false,
"tags": ["mongodb", "nosql", "mongodb-query"],
"title": "Is there merit to adding flat properties additional to a duplicate nested",
"descriptionMd": "Question description",
"offeredPoints": 10,
"slug": "is-there-merit-to-adding-flat-properties-additional-to-a-duplicate-nested",
"biddings": [{
"biddingId": "_biddingId",
"respondent": {
"userId": "fooId",
"firstName": "foo",
"lastName": "bar",
"points": "10",
"aboutMe": "Something about me"
},
"biddingPoints": 10,
"createdAt": "2019-03-21T08:00:00",
"lastUpdatedAt": "2019-03-21T08:00:00"
}],
"acceptedBidding": "_biddingId",
"answers": [
{
"respondent": {
"address": {
"userId": "fooId",
"firstName": "foo",
"lastName": "bar",
"points": "10",
"aboutMe": "Something about me"
}
},
"answerTextMd": "Answer 1",
"reviewRequested": true,
"reviewer": {
"userId": "fooId",
"firstName": "foo",
"lastName": "bar",
"points": "10",
"aboutMe": "Something about me"
},
"reviewStatus": "ANSWER_ACCEPTED",
"createdAt": "2019-03-21T08:00:00",
"lastUpdatedAt": "2019-03-21T08:00:00"
}
],
"createdAt": "2019-03-21T08:00:00",
"lastUpdatedAt": "2019-03-21T08:00:00"
}
This schema is meant for a Q&A forum and I prefer to keep all data embedded in the question document.
The requirements are as follows:
Timestamp created and updated are set on the Question document
Timestamp should be put on the nested biddings and answers as well
I know the default way to put a timestamp on the Question document:
const mySchema = new mongoose.Schema( {name: String}, {timestamps: true} );
How do I put the timestamp dynamically on the nested documents on update?
Is there an advised way of doing, or should I just put the fields there myself and update them manually?
You can also apply mongoose schema timestamps options to the inner schemas.
For example in the following schema, I applied timestamps: true option to the inner biddings schema.
const mongoose = require("mongoose");
const forumSchema = new mongoose.Schema(
{
title: { type: String, required: true },
biddings: [
{
type: new mongoose.Schema(
{
biddingId: String,
biddingPoints: Number
},
{ timestamps: true }
)
}
]
},
{ timestamps: true }
);
const Forum = mongoose.model("Forum", forumSchema);
module.exports = Forum;
Now let's test it:
I created a forum document with the following code:
const Forum = require("../models/forum");
router.post("/forums", async (req, res) => {
const result = await Forum.create(req.body);
res.send(result);
});
Request body:
{
"title": "Title 1",
"biddings": [
{
"biddingId": "bidding1",
"biddingPoints": 10
},
{
"biddingId": "bidding2",
"biddingPoints": 30
}
]
}
Response: (as you see timestamps are both applied to the parent and sub documents)
{
"_id": "5e3073b3a2890b03b029e92c",
"title": "Title 1",
"biddings": [
{
"_id": "5e3073b3a2890b03b029e92e",
"biddingId": "bidding1",
"biddingPoints": 10,
"createdAt": "2020-01-28T17:47:31.376Z",
"updatedAt": "2020-01-28T17:47:31.376Z"
},
{
"_id": "5e3073b3a2890b03b029e92d",
"biddingId": "bidding2",
"biddingPoints": 30,
"createdAt": "2020-01-28T17:47:31.376Z",
"updatedAt": "2020-01-28T17:47:31.376Z"
}
],
"createdAt": "2020-01-28T17:47:31.376Z",
"updatedAt": "2020-01-28T17:47:31.376Z",
"__v": 0
}
Now let's update the bidding point with the _id:5e3073b3a2890b03b029e92e
router.put("/forums/:forumId/biddings/:biddingId",
async (req, res) => {
let points = req.body.points;
try {
let result = await Forum.findByIdAndUpdate(
req.params.forumId,
{
$set: {
"biddings.$[inner].biddingPoints": points
}
},
{
arrayFilters: [{ "inner._id": req.params.biddingId }],
new: true
}
);
if (!result) return res.status(404);
res.send(result);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
}
);
The url will be like this: http://.../forums/5e3073b3a2890b03b029e92c/biddings/5e3073b3a2890b03b029e92e
Request: (it means I want to update the points to 50 of the bidding with _id:5e3073b3a2890b03b029e92e:
{
"points": 50
}
Response: (as you see updatedAt field value of the updated bidding changed automatically from 2020-01-28T17:47:31.376Z to 2020-01-28T17:50:03.855Z )
{
"_id": "5e3073b3a2890b03b029e92c",
"title": "Title 1",
"biddings": [
{
"_id": "5e3073b3a2890b03b029e92e",
"biddingId": "bidding1",
"biddingPoints": 50,
"createdAt": "2020-01-28T17:47:31.376Z",
"updatedAt": "2020-01-28T17:50:03.855Z" ==> UPDATED
},
{
"_id": "5e3073b3a2890b03b029e92d",
"biddingId": "bidding2",
"biddingPoints": 30,
"createdAt": "2020-01-28T17:47:31.376Z",
"updatedAt": "2020-01-28T17:47:31.376Z"
}
],
"createdAt": "2020-01-28T17:47:31.376Z",
"updatedAt": "2020-01-28T17:50:03.855Z",
"__v": 0
}

Mongo Aggregation using $Max

I have a collection that stores history, i.e. a new document is created every time a change is made to the data, I need to extract fields based on the max value of a date field, however my query keeps returning either all of the dates or requires me to push the fields into an array which make the data hard to analyze for an end-user.
Expected output as CSV:
MAX(DATE), docID, url, type
1579719200216, 12371, www.foodnetwork.com, food
1579719200216, 12371, www.cnn.com, news,
1579719200216, 12371, www.wikipedia.com, info
Sample Doc:
{
"document": {
"revenueGroup": "fn",
"metaDescription": "",
"metaData": {
"audit": {
"lastModified": 1312414124,
"clientId": ""
},
"entities": [],
"docId": 1313943,
"url": ""
},
"rootUrl": "",
"taggedImages": {
"totalSize": 1,
"list": [
{
"image": {
"objectId": "woman-reaching-for-basket",
"caption": "",
"url": "",
"height": 3840,
"width": 5760,
"owner": "Facebook",
"alt": "Woman reaching for basket"
},
"tags": {
"totalSize": 4,
"list": []
}
}
]
},
"title": "The 8 Best Food Items of 2020",
"socialTitle": "The 8 Best Food Items of 2020",
"primaryImage": {
"objectId": "woman-reaching-for-basket.jpg",
"caption": "",
"url": "",
"height": 3840,
"width": 5760,
"owner": "Hero Images / Getty Images",
"alt": "Woman reaching for basket in laundry room"
},
"subheading": "Reduce your footprint with these top-performing diets",
"citations": {
"list": []
},
"docId": 1313943,
"revisionId": "1313943_1579719200216",
"templateType": "LIST",
"documentState": {
"activeDate": 579719200166,
"state": "ACTIVE"
}
},
"url": "",
"items": {
"totalSize": "",
"list": [
{
"type": "recipe",
"data": {
"comInfo": {
"list": [
{
"type": "food",
"id": "https://www.foodnetwork.com"
}
]
},
"type": ""
},
"id": 4,
"uuid": "1313ida-qdad3-42c3-b41d-223q2eq2j"
},
{
"type": "recipe",
"data": {
"comInfo": {
"list": [
{
"type": "news",
"id": "https://www.cnn.com"
},
{
"type": "info",
"id": "https://www.wikipedia.com"
}
]
},
"type": "PRODUCT"
},
"id": 11,
"uuid": "318231jc-da12-4475-8994-283u130d32"
}
]
},
"vertical": "food"
}
Below query:
db.collection.aggregate([
{
$match: {
vertical: "food",
"document.documentState.state": "ACTIVE",
"document.templateType": "LIST"
}
},
{
$unwind: "$document.items"
},
{
$unwind: "$document.items.list"
},
{
$unwind: "$document.items.list.contents"
},
{
$unwind: "$document.items.list.contents.list"
},
{
$match: {
"document.items.list.contents.list.type": "recipe",
"document.revenueGroup": "fn"
}
},
{
$sort: {
"document.revisionId": -1
}
},
{
$group: {
_id: {
_id: {
docId: "$document.docId",
date: {$max: "$document.revisionId"}
},
url: "$document.items.list.contents.list.data.comInfo.list.id",
type: "$document.items.list.contents.list.data.comInfo.list.type"
}
}
},
{
$project: {
_id: 1
}
},
{
$sort: {
"document.items.list.contents.list.id": 1, "document.revisionId": -1
}
}
], {
allowDiskUse: true
})
First of all, you need to go through the documentation of the $group aggregation here.
you should be doing this instead:
{
$group: {
"_id": "$document.docId"
"date": {
$max: "$document.revisionId"
},
"url": {
$first: "$document.items.list.contents.list.data.comInfo.list.id"
},
"type": {
$first:"$document.items.list.contents.list.data.comInfo.list.type"
}
}
}
This will give you the required output.

How to get data from relation's with data stored in array's of Id

I have a model batches which has data as
"id": {
"type": "string",
"id": true,
"defaultFn": "uuidv4"
},
"batchType": {
"type": "string",
"required": true,
"default": "COURSE"
},
"venue": {
"type": "string",
"required": true
},
and another Model say bookedBathes
"batchesId": {
"type": [
"string"
],
"required": true
},
and created a relations from batches model to bookedBatches model as
"relations": {
"bookedBatches": {
"type": "referencesMany",
"model": "bookedBatches",
"foreignKey": "batchesId",
"primaryKey": "id"
}
}
now I want all the batches with booked details that are stored as array's of Id in booked details from batch model
let reqObject = {
"where": {
"and": [{
userId: userId,
isActive: true
}]
},
"order": "createdAt DESC",
include: [
{{
relation:"bookedBatches"}}]
}
Batches.find(reqObject, (resError, resData) => {
if (resError) {
return cb(new HttpErrors.BadRequest(resError, {
expose: false
}))
}
return cb(null, resData);
})
But I am not getting any value can any one help to get the values through relation's
Thank You!
i have improved your code. Please try this
let reqObject = {
"where": {
"and": [{
userId: userId,
isActive: true
}]
},
"order": "createdAt DESC",
include: [
{
relation: "bookedBatches",
scope: { fields: ["id","batchesId"] }
}
]
}
Batches.find(reqObject, (resError, resData) => {
if (resError) {
return cb(new HttpErrors.BadRequest(resError, {
expose: false
}))
}
return cb(null, resData);
})