Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sequelize with NodeJS can't join tables with limit

I'm trying to implement a simple query that should look like this:

select * from property join entity_area on property.id=entity_area.entity_id and entity_area.area_id=1 where property.price>300000 limit 12

Pretty straightforward: I want to get the joined result and then to limit to 12.

In Sequelize i'm using the following function:

return models.property.findAll(
{
    where: ["price>=?", 300000],
    include: [
    {
        model:models.entity_area,
        where: { area_id:1 }
    }
    ],
    limit:12
})

But this code generates the following sql:

select property.*, entity_area.* from (select * from property where property.price>300000 limit 12) join entity_area on property.id=entity_area.entity_id and entity_area.area_id=1

Which has totally different logic from what i'm trying to do because in the generated sql it first gets any 12 results and then tries to join with entity_area, and of course the random 12 results don't necessarily match the entity_area, so i'm getting no results back.

Please suggest me a proper way of doing it. The property table is very massive, and i have to use the "limit" rather than getting all the results and slicing them in javascript. Also i wouldn't like to start using raw queries.

like image 884
yuriscom Avatar asked Sep 24 '14 16:09

yuriscom


1 Answers

Actually I found a solution myself. I think this is a bug in sequelize framework.
In the node_modules/sequelize/lib/dialect/abstract/query_generator.js there is a "selectQuery" function which has the following line:

subQuery = limit && (options.hasIncludeWhere || options.hasIncludeRequired || options.hasMultiAssociation) && options.subQuery !== false

First of all there is an option subQuery that could be passed as false to remove the subquery generation. Sequelize documentation does not have a word about it. But moreover if you pass subQuery:false in the findAll object it's not going to work because for some reason it's getting as underfined to the selectQuery function.
I tried something like:

return models.property.findAll(
{
    where: ["price>=?", 300000],
    include: [
    {
        model:models.entity_area,
        where: { area_id:1 }
    }
    ],
    limit:12,
    subQuery:false
})

and still got options.subQuery=undefined.

So i had to change the function in query_generator.js to be something like:

subQuery = limit && (options.hasIncludeWhere || options.hasIncludeRequired || options.hasMultiAssociation) && options.subQuery !== false && options.doSubQuery===true

So now by default it's not doing this ugly subquery unless i specify explicitely doSubQuery:true. And finally i got the proper query without subquery with limit.

like image 176
yuriscom Avatar answered Sep 22 '22 06:09

yuriscom