Connecting 3 separate tables with a single through table in Sequelize - mysql

I'm trying to figure out why this is not working as intended. Any insights would be appreciated. Here's my situation:
I have a legacy connection of two tables with a through table. Something like:
Product model
const Product = sequelize.define('Product', {
// model attributes
});
Product.associate = (models) => {
Product.belongsToMany(models.Client, { through: models.ProductOwner, as: 'Owners'});
}
Client model
const Client = sequelize.define('Client', {
// model attributes
});
Client.associate = (models) => {
Client.belongsToMany(models.Product, { through: models.ProductOwner, as: 'OwnedProducts'});
}
ProductOwner
const ProductOwner = sequelize.define('Product', {
// no attributes
});
These form the N:M association with the through table ProductOwner.
This allows me, for example, to easily add a client to a product or getting all products that an existing client owns.
product.addOwner(client);
client.getOwnedProducts();
Now, I have the need to establish another chain of ownership to products that is unrelated to client. However, since this is still ownership, I would like to use the ProductOwner through table. So I add the new model:
Company model
const Company = sequelize.define('Company', {
// model attributes
});
Company.associate = (models) => {
Company.belongsToMany(models.Product, { through: models.ProductOwner, as: 'OwnedProducts'})
}
And the new association to the Products model. I also write a migration to add the CompanyId to the ProductOwners table and verify that the new reference is built into the database.
Product model
const Product = sequelize.define('Product', {
// model attributes
});
Product.associate = (models) => {
Product.belongsToMany(models.Client, { through: models.ProductOwner, as: 'Owners'});
Product.belongsToMany(models.Company, {
through: models.ProductOwner, as: 'CompanyOwners'
});
}
Now, on my code, I should be able to write:
product.addCompanyOwner(company);
company.getOwnedProducts();
And indeed, using the product instance method to add a new company does not throw any errors. However, the CompanyId column in the ProductOwners through table is still NULL.
Logging the query generated by Sequelize I see that the references to ProductId and ClientId are there, but there is no mention of CompanyId. Looks as if it is not recognizing that a new reference exists from Sequelize's point of view. However, the instance methods do work...
Which brings me to the question of why do they work? I assume that, by working, Sequelize is indeed creating the associations. But if that is the case, then why does the value for CompanyId is not set with the query?
Even writing it explicitly does not produce the expect result of setting CompanyId...
db.ProductOwner.create({
ProductId: 1,
ClientId: 1
}) // works to set all values
db.ProductOwner.create({
ProductId: 1,
CompanyId: 1
}) // sets ProductId to 1, but CompanyId is still NULL
What am I missing?

Related

Apply search columns and export mat table to excel

I am trying to implement a reports feature in my application which pulls country wise reports for a person logged in. I am getting data from backend and displaying this in a mat table. I want to have different filters based on country, sectors, industries on this data and it should also have the feature to export it.. For the filtering i dont want to run a backend query everytime so i want to filter it in the frontend and export the given data. I tried the table_to_sheet option of xlsx but that only exports the first page of the table. My code:
Object:
export interface Reports {
projectId: string;
projectName: string;
industry: string;
sector: string;
}
my table looks like this
I want to update the data array every time I apply a filter and then export the same. Can anyone help.
I am assigning the table in ngonInit
ngOnInit(): void {
this.service.getReports(this.loggedUser, this.role).subscribe
(
(res: any) => {
this.showSpinner = false
this.showTable = true
console.log(res)
console.log(res.reportList)
console.log('filters: ' +res.reportFilter.countrySet)
this.dataSourceMyRequests = new MatTableDataSource(res.reportList) ;
this.countryList = res.reportFilter.countrySet
this.gcnList = res.reportFilter.countrySet
this.sectorList = res.reportFilter.countrySet
//this.dataSourceMyRequests.sort = this.sortRequest
//this.dataSourceMyRequests.paginator = this.paginatorRequest
this.showData = true;
},
(error) => {
this.showSpinner = false
this._snackbar.open('No data found!', 'OK');
console.log(error)
}
)
}
I want to write a filter method like below:
filter(searchField, searchValue){
}
For filtering build a frontend filter that gets applied to incoming array of items. After filtering show only filtered items in table.
If filter returns true, then filteredArray will include it.
filteredArray: any[];
observable.subscribe((allData) => {
this.filteredArrray = allData.filter((item) => {
if (item?.projectId === '123') {
return true;
} else if (item?.projectName === 'name') {
return true;
} else {
return false;
}
});
})
Then export filteredArray[] to xlsx: https://stackblitz.com/edit/angular-material-table-export-excel-xlsx?file=app%2FtableUtil.ts

Using Objection JS How can I select specific columns with withGraphFetched

I have this question:
How can I indicate which columns I want get from database with the withGraphFetched method, I have a BelongsToOneRelation and I want exclude some columns, this is my model:
module.exports = class ProveedorModel extends Model {
...
static relationMappings = {
empresa: {
relation: Model.BelongsToOneRelation,
modelClass: EmpresaModel,
join: {
from: 'proveedor.empresa_id',
to: 'empresa.id'
}
}
};
...
}
and in my controller I have this:
const payload = await ProveedorModel.query().withGraphFetched('empresa');
but table empresa has to many columns which I won't, so how can I filter?
you can specify filter property for your relationship
class Person extends Model {
static relationMappings = {
pets: {
relation: Model.OneToManyRelation,
modelClass: Animal,
filter: query => query.select('id', 'ownerId', 'name'),
join: {
from: 'Person.id',
to: 'Animal.ownerId'
}
}
}
}
ref: https://github.com/Vincit/objection.js/issues/70#issuecomment-175143072
Just wondering why objection doesn't query only columns mapped in tableMetadata (https://vincit.github.io/objection.js/api/model/static-methods.html#static-tablemetadata) when used withGraphFetched like it does for withGraphJoined
Alternatively, you could map just the properties you want with parsedatabasejson
(https://vincit.github.io/objection.js/api/model/instance-methods.html#parsedatabasejson) but your SQL query will bring them all.

How to prevent duplicate entities in many-to-many table with Angular

How can I prevent duplication of records with the same combinations of entities, here : id_product and id_customer. When I click "save relation" a relation (many-to-many) between product and customer is created and this relation has its own id, id_product and id_customer. Is there any solution to block creation of relation between product and customer if such combination already exists in MySQL database ?
public saveRelation = (relationFormValue) => {
const newRelation = {
id_product: relationFormValue.id_product ,
id_customer: relationFormValue.id_customer
};
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
maxWidth: "400px",
data: new ConfirmDialogModel("Please confirm",'Are you sure to save this relation ?')
});
dialogRef.afterClosed().subscribe(dialogResult => {
if (dialogResult==true) {
this.relationService.create(newRelation)
.subscribe(
response => {
this.dialogRef.close(true);
},
error => {
this.errorService.handleError(error);
});
}
});
}

How can I query in Bookshelf/Knex on a BelongsToMany join table?

I am running Bookshelf over MySQL in a Node application.
I have a model called Document and another called Tag, which are joined through a belongsToMany relationship via a table called "map_tag_document".
Document:
'use strict';
const bookshelf = require('../bootstrap/bookshelf_instance').bookshelf;
const Tag = require('./tag').model;
const Document = bookshelf.Model.extend({
tableName: 'document',
tags() {
return this.belongsToMany(Tag, 'map_tag_document', 'document_id', 'tag_id')
}
},
{
jsonColumns: ['data']
}
);
module.exports.model = Document;
Tag:
'use strict';
const bookshelf = require('../bootstrap/bookshelf_instance').bookshelf;
const Tag = bookshelf.Model.extend({
tableName: 'tag'
});
module.exports.model = Tag;
Tags have a "name" column.
How do I query documents based on a search string appearing in the names of the tags associated with them?
Currently, I'm querying like this:
await new Document()
.query((qb) => {
if (searchString)
qb.whereRaw(`(data->'$.description' LIKE "%${searchString}%" OR name LIKE "%${searchString}%")`)
})
.fetch({
withRelated: ['tags']
});
What's the proper syntax for querying on the joined table?
Figured it out-can do joins on the Knex querybuilder object, like this:
const documents = await new Document()
.query((qb) => {
qb.join('map_tag_document', 'document.id', '=', 'map_tag_document.document_id')
qb.join('tag', 'tag.id', '=', 'map_tag_document.tag_id')
if (searchString)
qb.andWhereRaw(`(document.data->'$.description' LIKE "%${searchString}%" OR document.name LIKE "%${searchString}%" OR tag.name LIKE "%${searchString}%")`)
})
.fetch({
withRelated: ['tags']
});

With Dexie, how to remove a value from an array field for all objects in the table?

From this question I can find all objects in my table where a value occurs in a field that is an array. Now I need to delete that value from that field, for all objects in that table.
For example, suppose an events table, with objects:
{ people: ['John', 'Bob', 'Sue'] }
{ people: ['Harry', 'Sue', 'Jim'] }
{ people: ['John', 'Bob', 'Elaine'] }
{ people: ['Jim', 'Bob', 'Sue'] }
Suppose I want to delete 'Sue' from the people field for all objects.
How is this done with Dexie?
Adding the following code in an async function would do it:
await db.events.where('people').equals('Sue').modify(x => {
// This callback is run for every match.
// Here you can modify the people property to remove Sue from it:
x.people = x.people.filter(p => p !== 'Sue');
});
Note: Assume the schema is indexing 'people' with multiEntry index:
const db = new Dexie("testdb");
db.version(3).stores({
events: 'id, *people'
});
References:
https://dexie.org/docs/Collection/Collection.modify()
https://dexie.org/docs/MultiEntry-Index