Operators
Sequelize provides a large number of operators to help you build complex queries. They are available in the Op
object, which can be imported from @sequelize/core
.
They can be used in two ways; either using sql.where
, or as a key in where POJOs:
import { Op } from '@sequelize/core';
Post.findAll({
where: {
commentCount: {
[Op.gt]: 2,
},
},
});
// or
Post.findAll({
where: sql.where(sql.attribute('commentCount'), Op.gt, 2),
});
Basic Operators
Implicit Operator
When using a POJO, you can omit the operator and Sequelize will infer it. Depending on the value,
Sequelize will use either the Op.eq
, Op.is
or Op.in
operators.
Using null as the value will make Sequelize use the Op.is
operator:
Post.findAll({
where: {
authorId: null,
},
});
SELECT * FROM "posts" WHERE "authorId" IS NULL;
Using an array as the value will make Sequelize use the Op.in
operator:
Post.findAll({
where: {
authorId: [2, 3],
},
});
SELECT * FROM "posts" WHERE "authorId" IN (2, 3);
Finally, using any other value will make Sequelize use the Op.eq
operator:
Post.findAll({
where: {
authorId: 2,
},
});
SELECT * FROM "posts" WHERE "authorId" = 2;
Equality Operator
Op.eq
and Op.ne
are the simple "equals" and "not equals" operators:
- Op.eq
- Op.ne
Post.findAll({
where: {
authorId: { [Op.eq]: 12 },
},
});
SELECT * FROM "posts" WHERE "authorId" = 12;
Post.findAll({
where: {
authorId: { [Op.ne]: 12 },
},
});
SELECT * FROM "posts" WHERE "authorId" <> 12;
As mentioned in the section about Implicit Operators, Sequelize can infer the Op.eq
operator if you omit it,
but there are cases where you need to explicitly use it, for instance when you want to do an equality check against an array, or a JSON object:
Post.findAll({
where: {
tags: {
[Op.eq]: ['cooking', 'food'],
},
jsonMetadata: {
[Op.eq]: { key: 'value' },
},
},
});
Produces:
SELECT * FROM "posts" WHERE "tags" = ARRAY['cooking', 'food'] AND "jsonMetadata" = '{"key": "value"}';
Whereas omitting the Op.eq
operator would produce the following:
SELECT * FROM "posts" WHERE "tags" IN ('cooking', 'food') AND "jsonMetadata"->'key' = 'value';
IS Operator
PostgreSQL | MariaDB | MySQL | MSSQL | SQLite | Snowflake | db2 | ibmi | |
IS NULL | ||||||||
IS true, IS false | [docs] |
The Op.is
and Op.isNot
operators are used to check for NULL
and boolean values:
- Op.is
- Op.isNot
Post.findAll({
where: {
authorId: { [Op.is]: null },
},
});
SELECT * FROM "posts" WHERE "authorId" IS NULL;
Post.findAll({
where: {
authorId: { [Op.isNot]: null },
},
});
SELECT * FROM "posts" WHERE "authorId" IS NOT NULL;
Comparison Operators
Op.gt
, Op.gte
, Op.lt
, Op.lte
are the comparison operators. They respectively mean:
Op.gt
: Greater thanOp.gte
: Greater than or equal toOp.lt
: Less thanOp.lte
: Less than or equal to
- Op.gt
- Op.gte
- Op.lt
- Op.lte
Post.findAll({
where: {
commentCount: { [Op.gt]: 10 },
},
});
SELECT * FROM "posts" WHERE "commentCount" > 10;
Post.findAll({
where: {
commentCount: { [Op.gte]: 10 },
},
});
SELECT * FROM "posts" WHERE "commentCount" >= 10;
Post.findAll({
where: {
commentCount: { [Op.lt]: 10 },
},
});
SELECT * FROM "posts" WHERE "commentCount" < 10;
Post.findAll({
where: {
commentCount: { [Op.lte]: 10 },
},
});
SELECT * FROM "posts" WHERE "commentCount" <= 10;
Sequelize does not include SQL Server's "not less than" (!<
) and "not greater than" operators (!>
).
See Custom Operators to learn how you can use them in your queries.
Between Operator
The Op.between
and Op.notBetween
operators are used to check if a value is between two values.
This operator takes an array of exactly two values (the lower and upper bounds):
- Op.between
- Op.notBetween
Post.findAll({
where: {
commentCount: { [Op.between]: [1, 10] },
},
});
SELECT * FROM "posts" WHERE "commentCount" BETWEEN 1 AND 10;
Post.findAll({
where: {
commentCount: { [Op.notBetween]: [1, 10] },
},
});
SELECT * FROM "posts" WHERE "commentCount" NOT BETWEEN 1 AND 10;
IN Operator
The Op.in
and Op.notIn
operators are used to check if a value is in a list of values:
- Op.in
- Op.notIn
Post.findAll({
where: {
authorId: { [Op.in]: [2, 3] },
},
});
SELECT * FROM "posts" WHERE "authorId" IN (2, 3);
Post.findAll({
where: {
authorId: { [Op.notIn]: [2, 3] },
},
});
SELECT * FROM "posts" WHERE "authorId" NOT IN (2, 3);
String Operators
LIKE Operator
The Op.like
and Op.notLike
operators are used to check if a value matches a pattern.
In supported dialects, you can also use Op.iLike
and Op.notILike
to perform case-insensitive matches.
- Op.like
- Op.notLike
- Op.iLike
- Op.notILike
Post.findAll({
where: {
title: { [Op.like]: '%The Fox & The Hound%' },
},
});
SELECT * FROM "posts" WHERE "title" LIKE '%The Fox & The Hound%';
Post.findAll({
where: {
title: { [Op.notLike]: '%The Fox & The Hound%' },
},
});
SELECT * FROM "posts" WHERE "title" NOT LIKE '%The Fox & The Hound%';
Post.findAll({
where: {
title: { [Op.iLike]: '%The Fox & The Hound%' },
},
});
SELECT * FROM "posts" WHERE "title" ILIKE '%The Fox & The Hound%';
Post.findAll({
where: {
title: { [Op.notILike]: '%The Fox & The Hound%' },
},
});
SELECT * FROM "posts" WHERE "title" NOT ILIKE '%The Fox & The Hound%';
Sequelize does not provide a way to escape characters in LIKE patterns yet. You will need to use a custom operator to do so:
import { Literal, sql, Expression } from '@sequelize/core';
function like(value: Expression, pattern: string, escape: string): Literal {
return sql`${value} LIKE ${pattern} ESCAPE ${escape}`;
}
Post.findAll({
where: like(sql.attribute('title'), 'Inflation is above 10\\%', '\\'),
});
SELECT * FROM "posts" WHERE "title" LIKE 'Inflation is above 10\\%' ESCAPE '\\';
Regexp Operator
The Op.regexp
and Op.notRegexp
operators are used to check if a value matches a regular expression.
In supported dialects, you can also use Op.iRegexp
and Op.notIRegexp
to perform case-insensitive matches.
- Op.regexp
- Op.notRegexp
- Op.iRegexp
- Op.notIRegexp
Post.findAll({
where: {
title: { [Op.regexp]: '^The Fox' },
},
});
SELECT * FROM "posts" WHERE "title" ~ '^The Fox';
Post.findAll({
where: {
title: { [Op.notRegexp]: '^The Fox' },
},
});
SELECT * FROM "posts" WHERE "title" !~ '^The Fox';
Post.findAll({
where: {
title: { [Op.iRegexp]: '^The Fox' },
},
});
SELECT * FROM "posts" WHERE "title" ~* '^The Fox';
Post.findAll({
where: {
title: { [Op.notIRegexp]: '^The Fox' },
},
});
SELECT * FROM "posts" WHERE "title" !~* '^The Fox';
Starts & Ends With Operator
The Op.startsWith
and Op.endsWith
operators are used to check if a value starts or ends with a string.
Their negated versions are Op.notStartsWith
and Op.notEndsWith
.
Unlike the LIKE
and REGEXP
operators, these operators are case-sensitive, and will do an exact match against the string,
not a pattern match.
These operators do not exist natively in most dialects, but are still available through Sequelize as Sequelize will generate the appropriate replacement.
Currently, Op.startsWith
and Op.endsWith
both use LIKE under the hood and do not escape LIKE pattern characters.
This is considered to be a bug and will be fixed in a future release.
Contains String Operator
The Op.substring
and Op.notSubstring
operators are used to check if a value contains a string.
Unlike the LIKE
and REGEXP
operators, these operators are case-sensitive, and will do an exact match against the string,
not a pattern match.
Just like Op.startsWith
and Op.endsWith
, these operators do not exist natively in most dialects,
but are still available through Sequelize as Sequelize.
Currently, Op.substring
uses LIKE under the hood and does not escape LIKE pattern characters.
This is considered to be a bug and will be fixed in a future release.
Array Operators
PostgreSQL | MariaDB | MySQL | MSSQL | SQLite | Snowflake | db2 | ibmi |
[docs] |
Array Contains Operator
Op.contains
and Op.contained
are used to check whether an array contains or is contained by another array.
- Op.contains
- Op.contained
Post.findAll({
where: {
tags: { [Op.contains]: ['popular', 'trending'] },
},
});
SELECT * FROM "posts" WHERE "tags" @> ARRAY['popular', 'trending'];
Post.findAll({
where: {
tags: { [Op.contained]: ['popular', 'trending'] },
},
});
SELECT * FROM "posts" WHERE "tags" <@ ARRAY['popular', 'trending'];
Array Overlap Operator
The Op.overlap
operator can be used to check whether two arrays have at least one value in common:
Post.findAll({
where: {
tags: { [Op.overlap]: ['popular', 'trending'] },
},
});
SELECT * FROM "posts" WHERE "tags" && ARRAY['popular', 'trending'];
Range Operators
PostgreSQL | MariaDB | MySQL | MSSQL | SQLite | Snowflake | db2 | ibmi |
[docs] |
Range Contains Operator
These operators check for the containment relationship between two ranges.
The Op.contains
operator returns true if the left range completely contains the other,
while the Op.contained
operator returns true if the left range is completely contained within the other.
The contained value can be a single element or a range.
- Op.contains
- Op.contained
Post.findAll({
where: {
// publishedDuring is a range of dates
// This checks if a single date is present in the range
publishedDuring: { [Op.contains]: new Date() },
},
});
SELECT * FROM "posts" WHERE "publishedDuring" @> '2020-01-01';
Post.findAll({
where: {
// publishedDuring is a range of dates
// This checks if a date range is fully contained within the publishedDuring range
publishedDuring: {
[Op.contains]: [new Date('2019-01-01'), new Date('2023-01-01')],
},
},
});
SELECT * FROM "posts" WHERE "publishedDuring" @> '[2019-01-01, 2023-01-01)';
Post.findAll({
where: {
// The left-hand value can be a range or a single value
publishedAt: {
[Op.contained]: [new Date('2019-01-01'), new Date('2023-01-01')],
},
},
});
SELECT * FROM "posts" WHERE "publishedAt" <@ '[2019-01-01, 2023-01-01)';
Range Overlap Operator
The Op.overlap
operator determines whether two ranges have at least one value in common.
Post.findAll({
where: {
publishedDuring: {
[Op.overlap]: [new Date('2019-01-01'), new Date('2023-01-01')],
},
},
});
SELECT * FROM "posts" WHERE "publishedDuring" && '[2019-01-01, 2023-01-01)';
Adjacent Ranges Operator
The Op.adjacent
operator checks whether two ranges are consecutive without gaps between them, nor overlaps.
Event.findAll({
where: {
occursDuring: {
[Op.adjacent]: [new Date('2019-01-01'), new Date('2023-01-01')],
},
},
});
SELECT * FROM "events" WHERE "occursDuring" -|- '[2019-01-01, 2023-01-01)';
Range Left/Right Operators
These operators can be used to check where the values of a range are located relative to the values of another range.
- Op.strictLeft
- Op.strictRight
- Op.noExtendRight
- Op.noExtendLeft
The Op.strictLeft
operator check whether all values of the left range are less than
any value of the other range.
Event.findAll({
where: {
occursDuring: {
[Op.strictLeft]: [new Date('2019-01-01'), new Date('2023-01-01')],
},
},
});
SELECT * FROM "events" WHERE "occursDuring" << '[2019-01-01, 2023-01-01)';
The Op.strictRight
operator check whether all values of the left range are greather than
any value of the other range.
Event.findAll({
where: {
occursDuring: {
[Op.strictRight]: [new Date('2019-01-01'), new Date('2023-01-01')],
},
},
});
SELECT * FROM "events" WHERE "occursDuring" >> '[2019-01-01, 2023-01-01)';
The Op.noExtendRight
operator check whether all values of the left range are less than or equal to
any value of the other range.
Event.findAll({
where: {
occursDuring: {
[Op.noExtendRight]: [new Date('2019-01-01'), new Date('2023-01-01')],
},
},
});
SELECT * FROM "events" WHERE "occursDuring" &< '[2019-01-01, 2023-01-01)';
The Op.noExtendLeft
operator check whether all values of the left range are greater than or equal to
any value of the other range.
Event.findAll({
where: {
occursDuring: {
[Op.noExtendLeft]: [new Date('2019-01-01'), new Date('2023-01-01')],
},
},
});
SELECT * FROM "events" WHERE "occursDuring" &> '[2019-01-01, 2023-01-01)';
JSONB Operators
PostgreSQL | MariaDB | MySQL | MSSQL | SQLite | Snowflake | db2 | ibmi |
[docs] |
Sequelize offers a convenient syntax to access nested JSON properties. That syntax works with JSONB operators as well. See Querying JSON for more information.
JSON Contains Operator
The Op.contains
and Op.contained
operators can be used to check whether a JSONB value contains another JSONB value.
- Op.contains
- Op.contained
Post.findAll({
where: {
meta: { [Op.contains]: { keywords: 'orm, javascript, sequelize' } },
},
});
SELECT * FROM "posts" WHERE "meta" @> '{"keywords": "orm, javascript, sequelize"}';
Post.findAll({
where: {
meta: { [Op.contained]: { keywords: 'orm, javascript, sequelize' } },
},
});
SELECT * FROM "posts" WHERE "meta" <@ '{"keywords": "orm, javascript, sequelize"}';
To lean more, read about JSONB containment.
Sequelize stringifies JSON values for you, so you can use JavaScript values instead of using JSON strings.
JSON Key Existence Operators
The Op.anyKeyExists
and Op.allKeysExist
check whether a JSONB value contains any or all of the given keys, respectively.
- Op.anyKeyExists
- Op.allKeysExist
Post.findAll({
where: {
meta: { [Op.anyKeyExists]: ['keywords', 'description'] },
},
});
SELECT * FROM "posts" WHERE "meta" ?| ARRAY['keywords', 'description'];
Post.findAll({
where: {
meta: { [Op.allKeysExist]: ['keywords', 'description'] },
},
});
SELECT * FROM "posts" WHERE "meta" ?& ARRAY['keywords', 'description'];
Misc Operators
TSQuery Matching Operator
PostgreSQL | MariaDB | MySQL | MSSQL | SQLite | Snowflake | db2 | ibmi |
[docs] |
The Op.match
operator is a postgres-specific operator that allows you to match a tsvector
against a tsquery
.
It is equal to the @@
postgres operator.
It is one of the only operators that do not accept JavaScript values. Instead you must provide a built tsquery
object, which
you can get using the sql
tag or the sql.fn
function:
import { sql } from '@sequelize/core';
Document.findAll({
where: {
// in this example, it is assumed that this attribute has been previously populated using
// postgres' to_tsvector function.
searchTsVector: {
[Op.match]: sql`to_tsquery('english', 'cat & rat')`,
},
},
});
SELECT * FROM "documents" WHERE "searchTsVector" @@ to_tsquery('english', 'cat & rat');
To learn more, see PostgreSQL's documentation on Full Text Search.
ALL
, ANY
, & VALUES
PostgreSQL | MariaDB | MySQL | MSSQL | SQLite | Snowflake | db2 | ibmi |
[docs] |
The Op.all
and Op.any
operators can be used to compare a single value to an array of values.
What makes these operators especially useful is that it can be combined with other operators:
import { Op } from '@sequelize/core';
Post.findAll({
where: {
title: {
// this will check that the title contains both the word "cat" and "dog"
[Op.iLike]: {
[Op.all]: ['%cat%', '%dog%'],
},
},
},
});
SELECT * FROM "posts" WHERE "title" ILIKE ALL (ARRAY['%cat%', '%dog%']::TEXT[]);
If no comparison operator is specified, this operator will use the equality operator by default:
import { Op } from '@sequelize/core';
Post.findAll({
where: {
authorId: {
// this will check that the authorId is equal to either 12 or 13
[Op.any]: [12, 13],
},
},
});
SELECT * FROM "posts" WHERE "authorId" = ANY (ARRAY[12, 13]::INTEGER[]);
One limitation of ARRAYS is that you cannot make one of the values dynamic. The entire array is the value being compared.
The Op.values
operator can be used to circumvent this limitation. It allows you to specify a list of values that include computed values:
import { Op } from '@sequelize/core';
Post.findAll({
where: {
authorId: {
[Op.any]: {
// Op.values can be used to specify a list of values that include computed values,
// like a column name.
[Op.values]: [12, sql`12 + 45`],
},
},
},
});
SELECT * FROM "posts" WHERE "authorId" = ANY (VALUES (12), (12 + 45));
Logical combinations
The Op.and
, Op.or
, and Op.not
operators can be used to combine multiple conditions.
AND
A single object can specify multiple properties to check. If you do not specify any combination operator,
Sequelize will default to joining them with Op.and
:
Post.findAll({
where: {
authorId: 12,
status: 'active',
},
});
SELECT * FROM "posts" WHERE "authorId" = 12 AND "status" = 'active';
The same is true for arrays of objects:
Post.findAll({
where: [{ authorId: 12 }, { status: 'active' }],
});
SELECT * FROM "posts" WHERE "authorId" = 12 AND "status" = 'active';
OR
To replace that implicit AND
with an OR
, you can use the Op.or
operator:
import { Op } from '@sequelize/core';
Post.findAll({
where: {
[Op.or]: {
authorId: 12,
status: 'active',
},
},
});
SELECT * FROM "posts" WHERE "authorId" = 12 OR "status" = 'active';
Of course, Op.or
also accepts arrays:
import { Op } from '@sequelize/core';
Post.findAll({
where: {
[Op.or]: [{ authorId: 12 }, { status: 'active' }],
},
});
SELECT * FROM "posts" WHERE "authorId" = 12 OR "status" = 'active';
When nesting objects inside of an Op.or
operator, the OR operator will only be applied to one level of nesting at a time.
import { Op } from '@sequelize/core';
Post.findAll({
where: {
[Op.or]: [
// These properties will be joined with `AND`
{
authorId: 12,
status: 'active',
},
{
commentCount: 12,
},
],
},
});
SELECT * FROM "posts" WHERE ("authorId" = 12 AND "status" = 'active') OR "commentCount" = 12;
NOT
The Op.not
operator can be used to negate a condition:
import { Op } from '@sequelize/core';
Post.findAll({
where: {
[Op.not]: {
authorId: 12,
status: 'active',
},
},
});
SELECT * FROM "posts" WHERE NOT ("authorId" = 12 AND "status" = 'active');
Where to use logical operators
Logical operators can be used both before and after the name of the attribute being compared. For instance, the following two queries are equivalent:
import { Op } from '@sequelize/core';
Post.findAll({
where: {
[Op.or]: [{ authorId: 12 }, { authorId: 24 }],
},
});
Post.findAll({
where: {
authorId: {
[Op.or]: [12, 24],
},
},
});
SELECT * FROM "posts" WHERE "authorId" = 12 OR "authorId" = 24;
However, is it forbidden to use logical operators inside of another non-logical operator. The following would result in an error:
import { Op } from '@sequelize/core';
Post.findAll({
where: {
authorId: {
// This is not allowed: OR must contain GT, not the other way around
[Op.gt]: {
[Op.or]: [12, 24],
},
},
},
});
Custom Operators
Thanks to the sql
tag, it is very easy to create custom operators. We recommend reading
the chapter on raw queries to learn more.
Here is an example of a LIKE
that supports escaping special LIKE characters:
import { Literal, sql, Expression } from '@sequelize/core';
function like(value: Expression, pattern: string, escape: string): Literal {
return sql`${value} LIKE ${pattern} ESCAPE ${escape}`;
}
Post.findAll({
where: [{ authorId: 12 }, like(sql.attribute('title'), 'Inflation is above 10\\%', '\\')],
});
SELECT * FROM "posts" WHERE "authorId" = 12 AND "title" LIKE 'Inflation is above 10\%' ESCAPE '\';