How to use query parameter as col name on sequelize query - sql

So, I have an Express server running Sequelize ORM. Client will answer some questions with radio button options, and the answers is passed as query parameters to the URL, so I can make a GET request to server.
The thing is: my req.query values are supposed to be the column names for my database table. I want to know if it's possible to get the response from the database using Sequelize, and passing the parameters as the column name of the table.
async indexAbrigo(req, res) {
try {
let abrigos = null
let type = req.query.type // type = 'periodoTEMPORARIO'
let reason = req.query.reason // reason = 'motivoRUA'
abrigos = await Abrigos.findAll({
attributes: ['idABRIGOS'],
where: {
//I want type and reason to be the parameters that I got from client
type: 'S',
reason: 'S'
}
})
}
res.send(abrigos)
}
This is not working, the result of the query is something like
(SELECT `idABRIGOS` FROM Abrigos WHERE `type` = `S` AND `reason` = `S`)
Instead, I need that type and reason get translated to their values, and these values will be passed to the SQL. Is it possible to do with Sequelize? Or is it even possible with any ORM for Node/Express?
Thanks.

Yes, you can do it with computed property names. It would look like this:
where: {
[type]: 'S',
[reason]: 'S'
}
Your node.js version must be compatible with ES6.

Thank you - I am passing parameters right from req.body and this worked perfectly for me so I am posting it in the event that it may save someone time.
app.post('/doFind', (req, res) => {
Abrigos.findAll(
{[req.body.field]: req.body.newVal},
{returning: true, where: {id: req.body.id}}
)
.then( () => {
res.status(200).end()
})
.catch(err => {
console.log("Err: " + err)
res.status(404).send('Error attempting to update database').end()
})
})

Related

Get Article list by subscription

I am trying to make a filter with which I could get subscription records
Entity 'Subscription'
export class Subscription {
#PrimaryColumn()
id: string;
#Column('uuid')
userId: string;
#Column('uuid')
targetUserId: string;
#CreateDateColumn()
createdAt: Date;
}
Filter
applyFilter(query: QueryArticle, qb: SelectQueryBuilder<Article>, userId?: string) {
if (query.filter) {
switch (query.filter) {
....
case 'subscriptions':
qb.select(
`article.authorId WHERE targetUserId IN (SELECT targetUserId FROM Subscription WHERE userId=${userId})`,
);
break;
}
}
return qb;
}
SQL code
Select * FROM article WHERE authorId=targetUserId IN (SELECT targetUserId FROM Subscription WHERE userId=userId)
Error
syntax error at or near "f5779e5" +3974ms
QueryFailedError: syntax error at or near "f5779e5"
How can I get all the posts of people followed by a person use TypeORM?
Thanks in advance for your answer!
DO NOT DO WHAT YOU ARE DOING. You are risking a SQL Injection. If you really want to do a manual query, you can do manager.query:
const output = manager.query('article."authorId" WHERE "targetUserId" IN (SELECT "targetUserId" FROM Subscription WHERE "userId" = :userId)',
{ userId: userId }
);
Notice the second parameter that contains parameters which is referenced by key with :userId. If you're using template strings for queries, you're probably doing something wrong.
If you want to use the QueryBuilder, then it's going to look a little different (more info on QueryBuilder here)
const output = articleRepo.createQueryBuilder('article')
.select('article.authorId')
.where('article."authorId" IN (SELECT "targetUserId" FROM subscription WHERE "userId" = :userId)',
{ userId: userId }
)
.getRawMany(); // If you remove the .select('article.authorId'), you can use .getMany()

How to make complex nested where conditions with typeORM?

I am having multiple nested where conditions and want to generate them without too much code duplication with typeORM.
The SQL where condition should be something like this:
WHERE "Table"."id" = $1
AND
"Table"."notAvailable" IS NULL
AND
(
"Table"."date" > $2
OR
(
"Table"."date" = $2
AND
"Table"."myId" > $3
)
)
AND
(
"Table"."created" = $2
OR
"Table"."updated" = $4
)
AND
(
"Table"."text" ilike '%search%'
OR
"Table"."name" ilike '%search%'
)
But with the FindConditions it seems not to be possible to make them nested and so I have to use all possible combinations of AND in an FindConditions array. And it isn't possible to split it to .where() and .andWhere() cause andWhere can't use an Object Literal.
Is there another possibility to achieve this query with typeORM without using Raw SQL?
When using the queryBuilder I would recommend using Brackets
as stated in the Typeorm doc: https://typeorm.io/#/select-query-builder/adding-where-expression
You could do something like:
createQueryBuilder("user")
.where("user.registered = :registered", { registered: true })
.andWhere(new Brackets(qb => {
qb.where("user.firstName = :firstName", { firstName: "Timber" })
.orWhere("user.lastName = :lastName", { lastName: "Saw" })
}))
that will result with:
SELECT ...
FROM users user
WHERE user.registered = true
AND (user.firstName = 'Timber' OR user.lastName = 'Saw')
I think you are mixing 2 ways of retrieving entities from TypeORM, find from the repository and the query builder. The FindConditions are used in the find function. The andWhere function is use by the query builder. When building more complex queries it is generally better/easier to use the query builder.
Query builder
When using the query build you got much more freedom to make sure the query is what you need it to be. With the where you are free to add any SQL as you please:
const desiredEntity = await connection
.getRepository(User)
.createQueryBuilder("user")
.where("user.id = :id", { id: 1 })
.andWhere("user.date > :date OR (user.date = :date AND user.myId = :myId)",
{
date: specificCreatedAtDate,
myId: mysteryId,
})
.getOne();
Note that depending on your used database the actual SQL that you use here needs to be compatible. With that could also come a possible draw back of using this method. You will tie your project to a specific database. Make sure to read up about the aliases for tables you can set if you are using relations this would be handy.
Repository
You already saw that this is much less comfortable. This is because the find function or more specific the findOptions are using objects to build the where clause. This makes is harder to implement a proper interface to implement nested AND and OR clauses side by side. There for (I assume) they have chosen to split AND and OR clauses. This makes the interface much more declarative and means the you have to pull your OR clauses to the top:
const desiredEntity = await repository.find({
where: [{
id: id,
notAvailable: Not(IsNull()),
date: MoreThan(date)
},{
id: id,
notAvailable: Not(IsNull()),
date: date
myId: myId
}]
})
I cannot imagin looking a the size of the desired query that this code would be very performant.
Alternatively you could use the Raw find helper. This would require you to rewrite your clause per field, since you will only get access to the one alias at a time. You could guess the column names or aliases but this would be very poor practice and very unstable since you cannot directly control this easily.
if you want to nest andWhere statements if a condition is meet here is an example:
async getTasks(filterDto: GetTasksFilterDto, user: User): Promise<Task[]> {
const { status, search } = filterDto;
/* create a query using the query builder */
// task is what refer to the Task entity
const query = this.createQueryBuilder('task');
// only get the tasks that belong to the user
query.where('task.userId = :userId', { userId: user.id });
/* if status is defined then add a where clause to the query */
if (status) {
// :<variable-name> is a placeholder for the second object key value pair
query.andWhere('task.status = :status', { status });
}
/* if search is defined then add a where clause to the query */
if (search) {
query.andWhere(
/*
LIKE: find a similar match (doesn't have to be exact)
- https://www.w3schools.com/sql/sql_like.asp
Lower is a sql method
- https://www.w3schools.com/sql/func_sqlserver_lower.asp
* bug: search by pass where userId; fix: () whole addWhere statement
because andWhere stiches the where class together, add () to make andWhere with or and like into a single where statement
*/
'(LOWER(task.title) LIKE LOWER(:search) OR LOWER(task.description) LIKE LOWER(:search))',
// :search is like a param variable, and the search object is the key value pair. Both have to match
{ search: `%${search}%` },
);
}
/* execute the query
- getMany means that you are expecting an array of results
*/
let tasks;
try {
tasks = await query.getMany();
} catch (error) {
this.logger.error(
`Failed to get tasks for user "${
user.username
}", Filters: ${JSON.stringify(filterDto)}`,
error.stack,
);
throw new InternalServerErrorException();
}
return tasks;
}
I have a list of
{
date: specificCreatedAtDate,
userId: mysteryId
}
My solution is
.andWhere(
new Brackets((qb) => {
qb.where(
'userTable.date = :date0 AND userTable.type = :userId0',
{
date0: dates[0].date,
userId0: dates[0].type,
}
);
for (let i = 1; i < dates.length; i++) {
qb.orWhere(
`userTable.date = :date${i} AND userTable.userId = :userId${i}`,
{
[`date${i}`]: dates[i].date,
[`userId${i}`]: dates[i].userId,
}
);
}
})
)
That will produce something similar
const userEntity = await repository.find({
where: [{
userId: id0,
date: date0
},{
id: id1,
userId: date1
}
....
]
})

Making a select with an array

Hello I have and Array with objects, each object have atributes that I need for an select:
In this case it is the result from another consult with typeorm
" const CompaniesRelation: Array = await getRepository(CompanyRelation).find({ where:{ UserId: data.UserId, IsActive: true} });"
Companies: Array = [{CompanyId="a"}{CompanyId="b"}{CompanyId="c"}];
I need to make an select of all the data that matches with the Ids that are into Companies so for that I need to make an SQL like it:
const CompanyData: Array = SELECT *
FROM Company
INNER JOIN Company.CompanyId = CompaniesRelation[].CompanyId;
but it throw me error in typing, ¿how can I acces to each objetc into the array for make that match?
At the final I should traduce it sql to typeOrm, but I new and solving first in SQL it should help me to traduce to typeorm
Okay great, let us consider what we have to work with right:
So first we have a statement that gets a list of companies like so:
const CompaniesRelation: Array = await getRepository(CompanyRelation).find({
where: {
UserId: data.UserId,
IsActive: true
}
});
which ends up with something like this:
[ { CompanyId: 'a' }, { CompanyId: 'b' }, { CompanyId: 'c' } ]
Now we want to get a list of companies from an SQL DB with these Company IDs.
So the query should look like this:
// so first we re map the relation to an array of strings...
const ids: Array<string> = CompaniesRelation.map(c => c.CompanyId);
// then use it in the query, note the string interpolation for the query
const query: string = `SELECT * FROM Company WHERE CompanyId IN(${JSON.stringify(ids).slice(1, -1)});`;
I don't think this will cover the scope of the problem you have, I hope it helps though...feel free to ask

BookshelfJS - 'withRelated' through relational table returns empty results

I've been trying to structure the relations in my database for more efficient querying and joins but after following the guides for '.belongsToMany', '.through' and '.belongsTo' I'm now getting empty results.
I've got a Sound model and a Keyword model which I want to model with a many-to-many relationship (each Sound can have multiple Keywords, and each Keyword can be related to multiple sounds). Based on the documentation '.belongsToMany' would be the relation to use here.
I've set up my models as follows, using a 'sound_keyword' relational table/SoundKeyword relational model (where each entry has it's own unique 'id', a 'soundID', and a 'keywordID'):
var Sound = bookshelf.Model.extend({
tableName: 'sounds',
keywords: function () {
return this.belongsToMany(Keyword, 'sound_keyword', 'id', 'id').through(SoundKeyword, 'id', 'soundID');
},
});
var Keyword = bookshelf.Model.extend({
tableName: 'keywords',
sounds: function () {
return this.belongsToMany(Sound, 'sound_keyword', 'id', 'id').through(SoundKeyword, 'id', 'keywordID');
}
});
where:
var SoundKeyword = bookshelf.Model.extend({
tableName: 'sound_keyword',
sound: function () {
return this.belongsTo(Sound, 'soundID');
},
keyword: function () {
return this.belongsTo(Keyword, 'keywordID');
}
});
From what I've read in the docs and the BookshelfJS GitHub page the above seems to be correct. Despite this when I run the following query I'm getting an empty result set (the Sound in question is related to 3 Keywords in the DB):
var results = await Sound
.where('id', soundID)
.fetch({
withRelated: ['keywords']
})
.then((result) => {
console.log(JSON.stringify(result.related('keywords')));
})
Where am I going wrong with this? Are the relationships not set up correctly (Possibly wrong foreign keys?)? Am I fetching related models incorrectly?
Happy to provide the Knex setup as needed.
UPDATED EDIT:
I had been using the Model-Registry Plugin from the start and had forgotten about it. As it turns out, while the below syntax is correct, it prefers syntax similar to the following (i.e. lowercase 'model', dropping the '.extends' and putting model names in quotes):
var Sound = bookshelf.model('Sound',{
tableName: 'sounds',
keywords: function () {
return this.belongsToMany('Keyword', 'sound_keyword', 'soundID', 'keywordID');
},
});
var Keyword = bookshelf.model('Keyword',{
tableName: 'keywords',
sounds: function () {
return this.belongsToMany('Sound', 'sound_keyword', 'keywordID', 'soundID');
}
});
Hope this can be of help to others.
Seems like removing the '.through' relation and changing the IDs in the '.belongsToMany' call did the trick (as below), though I'm not entirely sure why (the docs seem to imply belongsToMany and .through work well together - possibly redundant?)
var Sound = bookshelf.Model.extend({
tableName: 'sounds',
keywords: function () {
return this.belongsToMany(Keyword, 'sound_keyword', 'soundID', 'keywordID');
},
});
var Keyword = bookshelf.Model.extend({
tableName: 'keywords',
sounds: function () {
return this.belongsToMany(Sound, 'sound_keyword', 'keywordID', 'soundID');
}
});
I did try my original code with soundID and keywordId instead of 'id' (as below), but without the .through relation and that gave the same empty results.

PouchDB Query like sql

with CouchDB is possible do queries "like" SQL. http://guide.couchdb.org/draft/cookbook.html says that
How you would do this in SQL:
SELECT field FROM table WHERE value="searchterm"
How you can do this in CouchDB:
Use case: get a result (which can be a record or set of records) associated with a key ("searchterm").
To look something up quickly, regardless of the storage mechanism, an index is needed. An index is a data structure optimized for quick search and retrieval. CouchDB’s map result is stored in such an index, which happens to be a B+ tree.
To look up a value by "searchterm", we need to put all values into the key of a view. All we need is a simple map function:
function(doc) {
if(doc.value) {
emit(doc.value, null);
}
}
This creates a list of documents that have a value field sorted by the data in the value field. To find all the records that match "searchterm", we query the view and specify the search term as a query parameter:
/database/_design/application/_view/viewname?key="searchterm"
how can I do this with PouchDB? the API provide methods to create temp view, but how I can personalize the get request with key="searchterm"?
You just add your attribute settings to the options object:
var searchterm = "boop";
db.query({map: function(doc) {
if(doc.value) {
emit(doc.value, null);
}
}, { key: searchterm }, function(err, res) { ... });
see http://pouchdb.com/api.html#query_database for more info
using regex
import PouchDB from 'pouchdb';
import PouchDBFind from 'pouchdb-find';
...
PouchDB.plugin(PouchDBFind)
const db = new PouchDB(dbName);
db.createIndex({index: {fields: ['description']}})
....
const {docs, warning} = await db.find({selector: { description: { $regex: /OVO/}}})