Updating 3rd tier document in subarray - mongodb

I have a document as follows (in a Projects collection):
title: "title"
company: "company"
companyID: "companyID"
category: "category"
starred: false
createdAt: new Date
tasks: [
{
task:'something'
completed: false
taskID: Random.id()
}
{
task:'something'
completed: false
taskID: Random.id()
}
{
task:'something'
completed: false
taskID: Random.id()
}
]
I can set completed: true on a task using this method:
completeTask: (projectID, taskID, ifCompleted) ->
Projects.update {
_id: projectID
'tasks.taskID': taskID
}, $set: 'tasks.$.completed': ifCompleted
return
if I change my document to this:
{
title: 'title.com'
company: 'company'
companyID: Random.id()
category: 'category'
starred: false
createdAt: new Date
categories: [
{
completed: true
name: 'ideas'
_id: Random.
tasks: [
{task: 'something', completed: false, taskID: Random.id()}
{task: 'something', completed: false, taskID: Random.id()}
{task: 'something', completed: false, taskID: Random.id()}
]
}
{
completed: false
name: 'development'
_id: Random.id()
tasks: [
{task: 'something', completed: false, taskID: Random.id()}
{task: 'something', completed: false, taskID: Random.id()}
{task: 'something', completed: false, taskID: Random.id()}
]
}
]
}
how can I set completed: true on a task?
So far I have
completeTask: (projectID, categoryID, taskID, ifCompleted) ->
Projects.update {

Related

sequelize eager loading returns null for associated table record. Why?

I want to retrieve data from an associated table but the returned value is null.
This is the association
static associate(models) {
appointment.hasOne(models.member, {
foreignKey: "id",
sourceKey: "member_id",
constraints: false,
});
appointment.hasOne(models.system_data, {
foreignKey: "id",
sourceKey: "facility_id",
constraints: false,
as: "system_data",
});
}
Members association is returned correctly but when I try to get system_data I get null even if it is present in database.
Here I try to get:
const getRelatedTableRecords = () =>
include?.split(",").map((table) => {
if (schemaName === "appointment" && table === "system_data") {
return { model: db[table], as: "system_data", required: false };
}
return { model: db[table] };
});
I don't understand why I can't get system_data. What might be the reasons?? Do you have any suggestions?
Member object
class Member extends Model {
static associate(models) {
// define association here
Member.hasMany(models.card, {
foreignKey: "card_id",
sourceKey: "card_id",
});
Member.hasMany(models.club, {
foreignKey: "id",
sourceKey: "club",
constraints: false,
});
Member.hasMany(models.schedule, {
foreignKey: "id",
sourceKey: "schedule",
constraints: false,
});
Member.hasMany(models.trainer, {
foreignKey: "id",
sourceKey: "trainer",
constraints: false,
});
Member.hasMany(models.file, {
foreignKey: "owner_id",
sourceKey: "id",
constraints: false,
});
Member.hasOne(models.facilities, {
foreignKey: "id",
sourceKey: "facility_id",
constraints: false,
});
}
}
Member.init(
{
first_name: {
type: DataTypes.STRING,
allowNull: false,
},
last_name: {
type: DataTypes.STRING,
allowNull: false,
},
tc_no: {
type: DataTypes.BIGINT,
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING,
},
card_id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
unique: true,
allowNull: false,
},
registered: {
type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false,
},
schedule: {
type: DataTypes.INTEGER,
references: {
model: "schedules",
key: "id",
},
onDelete: "CASCADE",
},
club: {
type: DataTypes.INTEGER,
references: {
model: "clubs",
key: "id",
},
onDelete: "CASCADE",
allowNull: true,
},
trainer: {
allowNull: true,
type: DataTypes.INTEGER,
references: {
model: "trainers",
key: "id",
},
onDelete: "CASCADE",
},
birthplace: {
type: DataTypes.STRING,
allowNull: true,
},
birthdate: { type: DataTypes.DATE, allowNull: true },
father_name: { type: DataTypes.STRING, allowNull: true },
mother_name: { type: DataTypes.STRING, allowNull: true },
gender: { type: DataTypes.STRING, allowNull: true },
profession: { type: DataTypes.STRING, allowNull: true },
address: { type: DataTypes.STRING, allowNull: true },
phone_number: { type: DataTypes.STRING, allowNull: true },
hes_code: { type: DataTypes.STRING, allowNull: true },
blood_type: { type: DataTypes.STRING, allowNull: true },
nationality: { type: DataTypes.STRING, allowNull: true },
profile_photo: {
type: DataTypes.STRING,
allowNull: true,
},
file: {
type: DataTypes.STRING,
allowNull: true,
},
session_ids: { type: DataTypes.ARRAY(DataTypes.STRING) },
facility_id: { type: DataTypes.INTEGER },
following: { type: DataTypes.ARRAY(DataTypes.INTEGER) },
creator: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
},
updater: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
},
},
{
sequelize,
modelName: "member",
hooks: {
afterCreate: (member, options) => {
sequelize.models.card.create({
card_id: member.card_id,
credits: 0,
creator: member.creator,
updater: member.updater,
});
},
beforeUpdate: (member, options) => {
const changed = member.changed();
if (changed?.includes("card")) {
sequelize.models.member.update(
{ trainer: null },
{ where: { id: member.dataValues.id } }
);
}
if (changed?.includes("trainer")) {
sequelize.models.member.update(
{ club: null },
{ where: { id: member.dataValues.id } }
);
}
},
},
}
);
system_data obj
class System_data extends Model {
static associate(models) {
// define association here
System_data.hasOne(models.facilities, {
foreignKey: "id",
sourceKey: "facility_id",
constraints: false,
});
}
}
System_data.init(
{
app_id: { type: DataTypes.STRING },
province: DataTypes.STRING,
district: DataTypes.STRING,
address: DataTypes.TEXT,
phone: DataTypes.STRING,
reset_credits: { type: DataTypes.BOOLEAN, defaultValue: false },
asset_path: {
type: DataTypes.STRING,
},
card_price: {
type: DataTypes.DECIMAL,
allowNull: false,
defaultValue: 0.0,
},
ticket_price: {
type: DataTypes.DECIMAL,
allowNull: false,
defaultValue: 0.0,
},
facility_id: { type: DataTypes.INTEGER },
working_hours: { type: DataTypes.STRING },
capacity: DataTypes.BIGINT,
season_based: { type: DataTypes.BOOLEAN, defaultValue: false },
appointment_based: { type: DataTypes.BOOLEAN, defaultValue: false },
seasons: {
type: DataTypes.ARRAY(DataTypes.STRING),
},
season_capacity: {
type: DataTypes.INTEGER,
},
},
{
sequelize,
modelName: "system_data",
}
);
As I can see system_data has facility_id as a foreign key to appointment so for appointment it should be like this:
appointment.hasOne(models.system_data, {
foreignKey: "facility_id",
constraints: false,
as: "system_data",
});
In both associations of the pair hasOne/belongsTo you should indicate the same foreignKey option with the same value that should point to a foreign key field on N side table in 1:N relationship.
There is no need to indicate sourceKey as long as you use a primary key field of the 1 side table in 1:N relationship.

How to create this tsvector generated always as column with sequelize?

I see that sequelize has DataTypes.TSVECTOR for postgres dialect.
I have a column whose definition in raw SQL is as follows
tsvector GENERATED ALWAYS AS (((
setweight(to_tsvector('english'::regconfig, (COALESCE(title, ''::character varying))::text), 'A'::"char") ||
setweight(to_tsvector('english'::regconfig, COALESCE(summary, ''::text)), 'B'::"char")) ||
setweight(to_tsvector('english'::regconfig, (COALESCE(content, ''::character varying))::text), 'C'::"char")))
STORED
How can I define this in my sequelize model
const FeedItem = sequelize.define(
'FeedItem', {
feedItemId: {
type: DataTypes.UUID,
primaryKey: true,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
},
pubdate: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: sequelize.literal('CURRENT_TIMESTAMP'),
validate: {
isDate: true,
},
},
link: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: [0, 2047],
},
},
guid: {
type: DataTypes.STRING,
validate: {
len: [0, 2047],
},
},
title: {
type: DataTypes.TEXT,
allowNull: false,
validate: {
len: [0, 65535],
},
},
summary: {
type: DataTypes.TEXT,
validate: {
len: [0, 65535],
},
},
content: {
type: DataTypes.TEXT,
validate: {
len: [0, 1048575],
},
},
author: {
type: DataTypes.STRING,
validate: {
len: [0, 63],
},
},
tags: {
type: DataTypes.ARRAY(DataTypes.STRING),
defaultValue: [],
},
// How to do that generated always part here???
searchable: {
type: DataTypes.TSVECTOR
},
}, {
timestamps: false,
underscored: true,
indexes: [
{
name: 'idx_feed_items_searchable',
fields: ['searchable'],
using: 'gin',
},
],
}
);
The model needs to be modified as follows to get this working
const FeedItem = sequelize.define(
'FeedItem',
{
feedItemId: {
type: DataTypes.UUID,
primaryKey: true,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
},
pubdate: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: sequelize.literal('CURRENT_TIMESTAMP'),
validate: {
isDate: true,
},
},
link: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: [0, 2047],
},
},
guid: {
type: DataTypes.STRING,
validate: {
len: [0, 2047],
},
},
title: {
type: DataTypes.TEXT,
allowNull: false,
validate: {
len: [0, 65535],
},
},
summary: {
type: DataTypes.TEXT,
validate: {
len: [0, 65535],
},
},
content: {
type: DataTypes.TEXT,
validate: {
len: [0, 1048575],
},
},
author: {
type: DataTypes.STRING,
validate: {
len: [0, 63],
},
},
tags: {
type: DataTypes.ARRAY(DataTypes.STRING),
defaultValue: [],
},
// https://stackoverflow.com/questions/67051281/use-postgres-generated-columns-in-sequelize-model
searchable: {
type: `tsvector GENERATED ALWAYS AS (((setweight(to_tsvector('english'::regconfig, (COALESCE(title, ''::character varying))::text), 'A'::"char") || setweight(to_tsvector('english'::regconfig, COALESCE(summary, ''::text)), 'B'::"char")) || setweight(to_tsvector('english'::regconfig, (COALESCE(content, ''::character varying))::text), 'C'::"char"))) STORED`,
set() {
throw new Error('generatedValue is read-only');
},
},
},
{
timestamps: false,
underscored: true,
indexes: [
{
name: 'idx_feed_items_pubdate_feed_item_id_desc',
fields: [
{ attribute: 'pubdate', order: 'DESC' },
{ attribute: 'feed_item_id', order: 'DESC' },
],
},
{
name: 'idx_feed_items_tags',
fields: ['tags'],
using: 'gin',
},
{
name: 'idx_feed_items_searchable',
fields: ['searchable'],
using: 'gin',
},
],
}
);
Does not work with sequelize.sync({alter: true}) you have to force:true or sequelize migrations

group documents by array of objects

Documents example
{
_id: 1,
entity: 'Transaction',
approvals: [
{
userId: 1, // ObjectID
final: true,
approved: false,
date: ISODate('2021-01-01'),
},
{
userId: 1,
final: true,
approved: false,
date: ISODate('2021-02-01'),
}
]
},
{
_id: 2,
entity: 'Transaction',
approvals: [
{
userId: 1,
final: true,
approved: false,
date: ISODate('2021-01-01'),
},
{
userId: 1,
final: true,
approved: false,
date: ISODate('2021-02-01'),
}
]
},
{
_id: 3,
entity: 'Transaction',
approvals: [
{
userId: 1,
final: true,
approved: true,
date: ISODate('2021-01-01'),
},
{
userId: 2,
final: true,
approved: false,
date: ISODate('2021-02-01'),
}
]
},
{
_id: 4,
entity: 'Transaction',
approvals: [
{
userId: 1,
final: true,
approved: true,
date: ISODate('2021-01-01'),
},
{
userId: 2,
final: true,
approved: false,
date: ISODate('2021-02-01'),
}
]
}
I want to group such documents on the following criteria:
entity
approvals.approved
approvals.userId
approvals.final
approvals.date
approvals array can contain multiple values, probably unsorted, where as I need to sort them by date; in case number of approvals is different - two objects belong to different groups.
Is that possible to do with use of Mongodb 4.2 facilities and what the most efficient way would be? I was thinking about a hash generation but not sure MongoDB has such function.
Desired result:
{
ids: [1, 2],
entity: 'Transaction',
approvals: [
{
userId: 1,
final: true,
approved: false,
date: ISODate('2021-01-01'),
},
{
userId: 1,
final: true,
approved: false,
date: ISODate('2021-02-01'),
}
]
}
{
ids: [3, 4],
entity: 'Transaction',
approvals: [
{
userId: 1,
final: true,
approved: true,
date: ISODate('2021-01-01'),
},
{
userId: 2,
final: true,
approved: false,
date: ISODate('2021-02-01'),
}
]
}
Maybe something like this:
mongos> db.h.aggregate([ { $unwind:"$approvals"} , {$group:{ _id:{ ent:"$entity" , app:"$approvals.approved" , fin:"$approvals.final" ,dat:"$approvals.date" } ,count:{$sum:1} }} , {$sort:{dat:1}} ])
{ "_id" : { "ent" : "Transaction", "app" : true, "fin" : true, "dat" : ISODate("2021-02-01T19:35:37.883Z") }, "count" : 1 }
{ "_id" : { "ent" : "Transaction", "app" : true, "fin" : true, "dat" : ISODate("2021-02-01T19:41:37.931Z") }, "count" : 1 }

how to query mongoose based on OR condition

i have a transaction collection and i require to query this from mongoose
_id: 5ecba0d446d0354084ad0b89
amount: 3
userId: 5ec3285cc7762963c88db765
type: 4
userType: 1
recipientId: 5ec328f2c7762963c88db768
status: "succeeded"
createdAt: 2020-05-25T10:41:24.449+00:00
updatedAt: 2020-05-25T10:41:24.449+00:00
__v: 0
_id: 5ecba0d446d0354084ad0b92
amount: 4
userId: 5ec3285cc7762963c88db888
type: 4
userType: 1
recipientId: 5ec3285cc7762963c88db765
status: "succeeded"
createdAt: 2020-05-25T10:41:24.449+00:00
updatedAt: 2020-05-25T10:41:24.449+00:00
__v: 0
_id: 5ecba0d446d0354084ad0b97
amount: 8
userId: 5ec3285cc7762963c88db332
type: 4
userType: 1
recipientId: 5ec328f2c7762963c88db589
status: "succeeded"
createdAt: 2020-05-25T10:41:24.449+00:00
updatedAt: 2020-05-25T10:41:24.449+00:00
__v: 0
how to query this such that i can get the transactions based on following condition
userId = 5ec3285cc7762963c88db765 or recipientId = 5ec3285cc7762963c88db765
and type = 4
and userType = 1
use $or and $and operators
model.find({
$or: [
{ userId: 5ec3285cc7762963c88db765 },
{ recipientId: 5ec3285cc7762963c88db765 }
],
$and: [ { type: 4 }, { userType: 1 } ]
});

Mongoose update push, delete in array

I have a mongoose model:
var schema = new Schema({
loginName: {
type: String,
unique: true,
required: true
},
hashedPassword: {
type: String,
required: true
},
salt: {
type: String,
required: true
},
created: {
type: Date,
default: Date.now
},
rooms: [{ _id: Schema.Types.ObjectId, loginName: [{ type: String }] }]
});
Example result:
{
_id: "56c0a986eeb118741109a45f",
loginName: "MegaDaddgy",
hashedPassword: "*****",
salt: "******",
__v: 10,
rooms: [
{
_id: "56c0a986eeb118741109a461",
loginName: [
"MegaDaddgy"
]
},
{
_id: "56c0d9e332f6ddc80ec7271c",
loginName: [
"MegaDaddgy"
]
}
],
created: "2016-02-14T16:21:26.272Z"
}
What I need:
search on field : rooms._id in every user document,
push new loginName in array loginName in every found user document
delete selected loginName in array
Example params:
rooms._id : 56c0a986eeb118741109a461
loginName: "John"
Result:
{
_id: "56c0a986eeb118741109a45f",
loginName: "MegaDaddgy",
hashedPassword: "*****",
salt: "******",
__v: 10,
rooms: [
{
_id: "56c0a986eeb118741109a461",
loginName: [
"MegaDaddgy", "John"
]
},
{
_id: "56c0d9e332f6ddc80ec7271c",
loginName: [
"MegaDaddgy"
]
}
],
created: "2016-02-14T16:21:26.272Z"
}
Example params:
rooms._id : 56c0a986eeb118741109a461
loginName: "John"
Result:
{
_id: "56c0a986eeb118741109a45f",
loginName: "MegaDaddgy",
hashedPassword: "*****",
salt: "******",
__v: 10,
rooms: [
{
_id: "56c0a986eeb118741109a461",
loginName: [
"MegaDaddgy"
]
},
{
_id: "56c0d9e332f6ddc80ec7271c",
loginName: [
"MegaDaddgy"
]
}
],
created: "2016-02-14T16:21:26.272Z"
}
How can I do this?
You could push John into loginName array through $push
Model.update({'rooms._id': ObjectId('56c0a986eeb118741109a461')},
{$push: {'rooms.$.loginName': 'John'}}, function(...));
delete John from loginName array through $pull
Model.update({'rooms._id': ObjectId('56c0a986eeb118741109a461')},
{$pull: {'rooms.$.loginName': 'John'}}, function(...));