diff --git a/docs/find-options.md b/docs/find-options.md index 83446d2f7b..6fb3bb7a49 100644 --- a/docs/find-options.md +++ b/docs/find-options.md @@ -46,6 +46,23 @@ Querying a column from an embedded entity should be done with respect to the hie userRepository.find({ where: { name: { first: "Timber", last: "Saw" } } }); ``` +Querying with OR operator: + +```typescript +userRepository.find({ + where: [ + { firstName: "Timber", lastName: "Saw" }, + { firstName: "Stan", lastName: "Lee" } + ] +}); +``` + +will execute following query: + +```sql +SELECT * FROM "user" WHERE ("firstName" = 'Timber' AND "lastName" = 'Saw') OR ("firstName" = 'Stan' AND "lastName" = 'Lee') +``` + * `order` - selection order. ```typescript @@ -154,6 +171,22 @@ will execute following query: SELECT * FROM "post" WHERE "likes" < 10 ``` +* `LessThanOrEqual` + +```ts +import {LessThanOrEqual} from "typeorm"; + +const loadedPosts = await connection.getRepository(Post).find({ + likes: LessThanOrEqual(10) +}); +``` + +will execute following query: + +```sql +SELECT * FROM "post" WHERE "likes" <= 10 +``` + * `MoreThan` ```ts @@ -170,6 +203,22 @@ will execute following query: SELECT * FROM "post" WHERE "likes" > 10 ``` +* `MoreThanOrEqual` + +```ts +import {MoreThanOrEqual} from "typeorm"; + +const loadedPosts = await connection.getRepository(Post).find({ + likes: MoreThanOrEqual(10) +}); +``` + +will execute following query: + +```sql +SELECT * FROM "post" WHERE "likes" >= 10 +``` + * `Equal` ```ts diff --git a/src/cache/QueryResultCacheFactory.ts b/src/cache/QueryResultCacheFactory.ts index 3af068c10a..394b3cd66d 100644 --- a/src/cache/QueryResultCacheFactory.ts +++ b/src/cache/QueryResultCacheFactory.ts @@ -32,8 +32,10 @@ export class QueryResultCacheFactory { if ((this.connection.options.cache as any).type === "ioredis") return new RedisQueryResultCache(this.connection, "ioredis"); + if ((this.connection.options.cache as any).type === "ioredis/cluster") + return new RedisQueryResultCache(this.connection, "ioredis/cluster"); + return new DbQueryResultCache(this.connection); } - } diff --git a/src/cache/RedisQueryResultCache.ts b/src/cache/RedisQueryResultCache.ts index 1625c381ce..39202074e7 100644 --- a/src/cache/RedisQueryResultCache.ts +++ b/src/cache/RedisQueryResultCache.ts @@ -26,13 +26,13 @@ export class RedisQueryResultCache implements QueryResultCache { /** * Type of the Redis Client (redis or ioredis). */ - protected clientType: "redis" | "ioredis"; + protected clientType: "redis" | "ioredis" | "ioredis/cluster"; // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- - constructor(protected connection: Connection, clientType: "redis" | "ioredis") { + constructor(protected connection: Connection, clientType: "redis" | "ioredis" | "ioredis/cluster") { this.clientType = clientType; this.redis = this.loadRedis(); } @@ -61,6 +61,13 @@ export class RedisQueryResultCache implements QueryResultCache { this.client = new this.redis(); } + } else if (this.clientType === "ioredis/cluster") { + if (cacheOptions && cacheOptions.options) { + this.client = new this.redis.Cluster(cacheOptions.options); + } else { + throw new Error(`Options required for ${this.clientType}.`); + } + } } diff --git a/src/connection/BaseConnectionOptions.ts b/src/connection/BaseConnectionOptions.ts index ffffb27808..2b9b18afee 100644 --- a/src/connection/BaseConnectionOptions.ts +++ b/src/connection/BaseConnectionOptions.ts @@ -114,7 +114,7 @@ export interface BaseConnectionOptions { * - "database" means cached values will be stored in the separate table in database. This is default value. * - "redis" means cached values will be stored inside redis. You must provide redis connection options. */ - readonly type?: "database"|"redis"; // todo: add mongodb and other cache providers as well in the future + readonly type?: "database" | "redis" | "ioredis" | "ioredis/cluster"; // todo: add mongodb and other cache providers as well in the future /** * Used to provide redis connection options. diff --git a/src/connection/Connection.ts b/src/connection/Connection.ts index 3bb99ca3e2..774fe914b8 100644 --- a/src/connection/Connection.ts +++ b/src/connection/Connection.ts @@ -355,7 +355,7 @@ export class Connection { * Wraps given function execution (and all operations made there) into a transaction. * All database operations must be executed using provided entity manager. */ - async transaction(runInTransaction: (entityManager: EntityManager) => Promise): Promise { + async transaction(runInTransaction: (entityManager: EntityManager) => Promise): Promise { return this.manager.transaction(runInTransaction); } diff --git a/src/driver/mongodb/MongoConnectionOptions.ts b/src/driver/mongodb/MongoConnectionOptions.ts index 1c59a9c0bb..f8fe8e426e 100644 --- a/src/driver/mongodb/MongoConnectionOptions.ts +++ b/src/driver/mongodb/MongoConnectionOptions.ts @@ -3,6 +3,7 @@ import {ReadPreference} from "./typings"; /** * MongoDB specific connection options. + * Synced with http://mongodb.github.io/node-mongodb-native/3.1/api/MongoClient.html */ export interface MongoConnectionOptions extends BaseConnectionOptions { @@ -73,7 +74,7 @@ export interface MongoConnectionOptions extends BaseConnectionOptions { * String or buffer containing the certificate private key we wish to present * (needs to have a mongod server with ssl support, 2.4 or higher) */ - readonly sslKey?: string|Buffer; + readonly sslKey?: string; /** * String or buffer containing the certificate password @@ -81,6 +82,12 @@ export interface MongoConnectionOptions extends BaseConnectionOptions { */ readonly sslPass?: string|Buffer; + /** + * SSL Certificate revocation list binary buffer + * (needs to have a mongod server with ssl support, 2.4 or higher) + */ + readonly sslCRL?: string|Buffer; + /** * Reconnect on error. Default: true */ @@ -101,6 +108,12 @@ export interface MongoConnectionOptions extends BaseConnectionOptions { */ readonly connectTimeoutMS?: number; + /** + * Version of IP stack. Can be 4, 6. + * If undefined, will attempt to connect with IPv6, and will fall back to IPv4 on failure + */ + readonly family?: number; + /** * TCP Socket timeout setting. Default: 360000 */ @@ -117,12 +130,12 @@ export interface MongoConnectionOptions extends BaseConnectionOptions { readonly reconnectInterval?: number; /** - * Turn on high availability monitoring. Default true + * Control if high availability monitoring runs for Replicaset or Mongos proxies. Default true */ readonly ha?: boolean; /** - * Time between each replicaset status check. Default: 10000,5000 + * The High availability period for replicaset inquiry. Default: 10000 */ readonly haInterval?: number; @@ -212,13 +225,13 @@ export interface MongoConnectionOptions extends BaseConnectionOptions { * Sets a cap on how many operations the driver will buffer up before giving up on getting a working connection, * default is -1 which is unlimited. */ - readonly bufferMaxEntries?: boolean; + readonly bufferMaxEntries?: number; /** * The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, * ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). */ - readonly readPreference?: ReadPreference; + readonly readPreference?: ReadPreference|string; /** * A primary key factory object for generation of custom _id keys. @@ -245,13 +258,67 @@ export interface MongoConnectionOptions extends BaseConnectionOptions { */ readonly loggerLevel?: "error"|"warn"|"info"|"debug"; + // Do not overwrite BaseConnectionOptions.logger + // readonly logger?: any; + + /** + * Ensure we check server identify during SSL, set to false to disable checking. Only works for Node 0.12.x or higher. You can pass in a boolean or your own checkServerIdentity override function + * Default: true + */ + readonly checkServerIdentity?: boolean|Function; + + /** + * Validate MongoClient passed in options for correctness. Default: false + */ + readonly validateOptions?: boolean|any; + + /** + * The name of the application that created this MongoClient instance. MongoDB 3.4 and newer will print this value in the server log upon establishing each connection. It is also recorded in the slow query log and profile collections + */ + readonly appname?: string; + /** * Sets the authentication mechanism that MongoDB will use to authenticate the connection */ readonly authMechanism?: string; /** - * Specify new url parser usage + * Type of compression to use: snappy or zlib + */ + readonly compression?: any; + + /** + * Specify a file sync write concern. Default: false + */ + readonly fsync?: boolean; + + /** + * Read preference tags + */ + readonly readPreferenceTags?: any[]; + + /** + * The number of retries for a tailable cursor. Default: 5 + */ + readonly numberOfRetries?: number; + + /** + * Enable auto reconnecting for single server instances. Default: true + */ + readonly auto_reconnect?: boolean; + + /** + * Enable command monitoring for this client. Default: false + */ + readonly monitorCommands?: boolean; + + /** + * If present, the connection pool will be initialized with minSize connections, and will never dip below minSize connections + */ + readonly minSize?: number; + + /** + * Determines whether or not to use the new url parser. Default: false */ readonly useNewUrlParser?: boolean; } diff --git a/src/driver/mongodb/MongoDriver.ts b/src/driver/mongodb/MongoDriver.ts index 4e387b0117..05f90ba75c 100644 --- a/src/driver/mongodb/MongoDriver.ts +++ b/src/driver/mongodb/MongoDriver.ts @@ -116,6 +116,72 @@ export class MongoDriver implements Driver { */ protected mongodb: any; + /** + * Valid mongo connection options + * NOTE: Keep sync with MongoConnectionOptions + * Sync with http://mongodb.github.io/node-mongodb-native/3.1/api/MongoClient.html + */ + protected validOptionNames: string[] = [ + "poolSize", + "ssl", + "sslValidate", + "sslCA", + "sslCert", + "sslKey", + "sslPass", + "sslCRL", + "autoReconnect", + "noDelay", + "keepAlive", + "keepAliveInitialDelay", + "connectTimeoutMS", + "family", + "socketTimeoutMS", + "reconnectTries", + "reconnectInterval", + "ha", + "haInterval", + "replicaSet", + "secondaryAcceptableLatencyMS", + "acceptableLatencyMS", + "connectWithNoPrimary", + "authSource", + "w", + "wtimeout", + "j", + "forceServerObjectId", + "serializeFunctions", + "ignoreUndefined", + "raw", + "bufferMaxEntries", + "readPreference", + "pkFactory", + "promiseLibrary", + "readConcern", + "maxStalenessSeconds", + "loggerLevel", + // Do not overwrite BaseConnectionOptions.logger + // "logger", + "promoteValues", + "promoteBuffers", + "promoteLongs", + "domainsEnabled", + "checkServerIdentity", + "validateOptions", + "appname", + // omit auth - we are building url from username and password + // "auth" + "authMechanism", + "compression", + "fsync", + "readPreferenceTags", + "numberOfRetries", + "auto_reconnect", + "minSize", + "monitorCommands", + "useNewUrlParser" + ]; + // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- @@ -139,56 +205,16 @@ export class MongoDriver implements Driver { */ connect(): Promise { return new Promise((ok, fail) => { - this.mongodb.MongoClient.connect(this.buildConnectionUrl(), { - poolSize: this.options.poolSize, - ssl: this.options.ssl, - sslValidate: this.options.sslValidate, - sslCA: this.options.sslCA, - sslCert: this.options.sslCert, - sslKey: this.options.sslKey, - sslPass: this.options.sslPass, - autoReconnect: this.options.autoReconnect, - noDelay: this.options.noDelay, - keepAlive: this.options.keepAlive, - connectTimeoutMS: this.options.connectTimeoutMS, - socketTimeoutMS: this.options.socketTimeoutMS, - reconnectTries: this.options.reconnectTries, - reconnectInterval: this.options.reconnectInterval, - ha: this.options.ha, - haInterval: this.options.haInterval, - replicaSet: this.options.replicaSet, - acceptableLatencyMS: this.options.acceptableLatencyMS, - secondaryAcceptableLatencyMS: this.options.secondaryAcceptableLatencyMS, - connectWithNoPrimary: this.options.connectWithNoPrimary, - authSource: this.options.authSource, - w: this.options.w, - wtimeout: this.options.wtimeout, - j: this.options.j, - forceServerObjectId: this.options.forceServerObjectId, - serializeFunctions: this.options.serializeFunctions, - ignoreUndefined: this.options.ignoreUndefined, - raw: this.options.raw, - promoteLongs: this.options.promoteLongs, - promoteBuffers: this.options.promoteBuffers, - promoteValues: this.options.promoteValues, - domainsEnabled: this.options.domainsEnabled, - bufferMaxEntries: this.options.bufferMaxEntries, - readPreference: this.options.readPreference, - pkFactory: this.options.pkFactory, - promiseLibrary: this.options.promiseLibrary, - readConcern: this.options.readConcern, - maxStalenessSeconds: this.options.maxStalenessSeconds, - loggerLevel: this.options.loggerLevel, - logger: this.options.logger, - authMechanism: this.options.authMechanism, - useNewUrlParser: this.options.useNewUrlParser - }, (err: any, client: any) => { - if (err) return fail(err); - - this.queryRunner = new MongoQueryRunner(this.connection, client); - ObjectUtils.assign(this.queryRunner, { manager: this.connection.manager }); - ok(); - }); + this.mongodb.MongoClient.connect( + this.buildConnectionUrl(), + this.buildConnectionOptions(), + (err: any, client: any) => { + if (err) return fail(err); + + this.queryRunner = new MongoQueryRunner(this.connection, client); + ObjectUtils.assign(this.queryRunner, { manager: this.connection.manager }); + ok(); + }); }); } @@ -394,4 +420,21 @@ export class MongoDriver implements Driver { return `mongodb://${credentialsUrlPart}${this.options.host || "127.0.0.1"}:${this.options.port || "27017"}/${this.options.database}`; } + /** + * Build connection options from MongoConnectionOptions + */ + protected buildConnectionOptions(): any { + const mongoOptions: any = {}; + + for (let index = 0; index < this.validOptionNames.length; index++) { + const optionName = this.validOptionNames[index]; + + if (optionName in this.options) { + mongoOptions[optionName] = (this.options as any)[optionName]; + } + } + + return mongoOptions; + } + } diff --git a/src/error/EntityColumnNotFound.ts b/src/error/EntityColumnNotFound.ts new file mode 100644 index 0000000000..2261979a8d --- /dev/null +++ b/src/error/EntityColumnNotFound.ts @@ -0,0 +1,13 @@ +/** + * + */ +export class EntityColumnNotFound extends Error { + name = "EntityColumnNotFound"; + + constructor(propertyPath: string) { + super(); + Object.setPrototypeOf(this, EntityColumnNotFound.prototype); + this.message = `No entity column "${propertyPath}" was found.`; + } + +} diff --git a/src/error/UpdateValuesMissingError.ts b/src/error/UpdateValuesMissingError.ts new file mode 100644 index 0000000000..96580eaf81 --- /dev/null +++ b/src/error/UpdateValuesMissingError.ts @@ -0,0 +1,13 @@ +/** + * Thrown when user tries to update using QueryBuilder but do not specify what to update. + */ +export class UpdateValuesMissingError extends Error { + name = "UpdateValuesMissingError"; + + constructor() { + super(); + Object.setPrototypeOf(this, UpdateValuesMissingError.prototype); + this.message = `Cannot perform update query because update values are not defined. Call "qb.set(...)" method to specify updated values.`; + } + +} diff --git a/src/find-options/FindOperator.ts b/src/find-options/FindOperator.ts index 64cd7428e8..f2b8a6c577 100644 --- a/src/find-options/FindOperator.ts +++ b/src/find-options/FindOperator.ts @@ -94,8 +94,12 @@ export class FindOperator { } case "lessThan": return `${aliasPath} < ${parameters[0]}`; + case "lessThanOrEqual": + return `${aliasPath} <= ${parameters[0]}`; case "moreThan": return `${aliasPath} > ${parameters[0]}`; + case "moreThanOrEqual": + return `${aliasPath} >= ${parameters[0]}`; case "equal": return `${aliasPath} = ${parameters[0]}`; case "like": diff --git a/src/find-options/FindOperatorType.ts b/src/find-options/FindOperatorType.ts index a8515a28c4..3830dec877 100644 --- a/src/find-options/FindOperatorType.ts +++ b/src/find-options/FindOperatorType.ts @@ -3,7 +3,9 @@ */ export type FindOperatorType = "not" | "lessThan" + | "lessThanOrEqual" | "moreThan" + | "moreThanOrEqual" | "equal" | "between" | "in" diff --git a/src/find-options/operator/LessThanOrEqual.ts b/src/find-options/operator/LessThanOrEqual.ts new file mode 100644 index 0000000000..fc8eeb7580 --- /dev/null +++ b/src/find-options/operator/LessThanOrEqual.ts @@ -0,0 +1,9 @@ +import {FindOperator} from "../FindOperator"; + +/** + * Find Options Operator. + * Example: { someField: LessThanOrEqual(10) } + */ +export function LessThanOrEqual(value: T|FindOperator) { + return new FindOperator("lessThanOrEqual", value); +} diff --git a/src/find-options/operator/MoreThanOrEqual.ts b/src/find-options/operator/MoreThanOrEqual.ts new file mode 100644 index 0000000000..7c3ca32e39 --- /dev/null +++ b/src/find-options/operator/MoreThanOrEqual.ts @@ -0,0 +1,9 @@ +import {FindOperator} from "../FindOperator"; + +/** + * Find Options Operator. + * Example: { someField: MoreThanOrEqual(10) } + */ +export function MoreThanOrEqual(value: T|FindOperator) { + return new FindOperator("moreThanOrEqual", value); +} diff --git a/src/index.ts b/src/index.ts index 94308e8a72..b25708a545 100644 --- a/src/index.ts +++ b/src/index.ts @@ -78,8 +78,10 @@ export * from "./find-options/operator/Equal"; export * from "./find-options/operator/In"; export * from "./find-options/operator/IsNull"; export * from "./find-options/operator/LessThan"; +export * from "./find-options/operator/LessThanOrEqual"; export * from "./find-options/operator/Like"; export * from "./find-options/operator/MoreThan"; +export * from "./find-options/operator/MoreThanOrEqual"; export * from "./find-options/operator/Not"; export * from "./find-options/operator/Raw"; export * from "./find-options/FindConditions"; diff --git a/src/platform/PlatformTools.ts b/src/platform/PlatformTools.ts index 9479d79f69..423e5a0e5a 100644 --- a/src/platform/PlatformTools.ts +++ b/src/platform/PlatformTools.ts @@ -83,6 +83,7 @@ export class PlatformTools { * ioredis */ case "ioredis": + case "ioredis/cluster": return require("ioredis"); /** diff --git a/src/query-builder/UpdateQueryBuilder.ts b/src/query-builder/UpdateQueryBuilder.ts index bab54ff303..322bf2fe35 100644 --- a/src/query-builder/UpdateQueryBuilder.ts +++ b/src/query-builder/UpdateQueryBuilder.ts @@ -17,6 +17,8 @@ import {AbstractSqliteDriver} from "../driver/sqlite-abstract/AbstractSqliteDriv import {OrderByCondition} from "../find-options/OrderByCondition"; import {LimitOnUpdateNotSupportedError} from "../error/LimitOnUpdateNotSupportedError"; import {OracleDriver} from "../driver/oracle/OracleDriver"; +import {UpdateValuesMissingError} from "../error/UpdateValuesMissingError"; +import {EntityColumnNotFound} from "../error/EntityColumnNotFound"; /** * Allows to build complex sql queries in a fashion way and execute those queries. @@ -364,6 +366,11 @@ export class UpdateQueryBuilder extends QueryBuilder implements EntityMetadata.createPropertyPath(metadata, valuesSet).forEach(propertyPath => { // todo: make this and other query builder to work with properly with tables without metadata const columns = metadata.findColumnsWithPropertyPath(propertyPath); + + if (columns.length <= 0) { + throw new EntityColumnNotFound(propertyPath); + } + columns.forEach(column => { const paramName = "upd_" + column.databaseName; @@ -445,6 +452,10 @@ export class UpdateQueryBuilder extends QueryBuilder implements }); } + if (updateColumnAndValues.length <= 0) { + throw new UpdateValuesMissingError(); + } + // we re-write parameters this way because we want our "UPDATE ... SET" parameters to be first in the list of "nativeParameters" // because some drivers like mysql depend on order of parameters if (this.connection.driver instanceof MysqlDriver || @@ -512,7 +523,7 @@ export class UpdateQueryBuilder extends QueryBuilder implements if (this.expressionMap.valuesSet instanceof Object) return this.expressionMap.valuesSet; - throw new Error(`Cannot perform update query because update values are not defined. Call "qb.set(...)" method to specify inserted values.`); + throw new UpdateValuesMissingError(); } } diff --git a/test/functional/query-builder/update/query-builder-update.ts b/test/functional/query-builder/update/query-builder-update.ts index 1f154fa165..ec7d494aa2 100644 --- a/test/functional/query-builder/update/query-builder-update.ts +++ b/test/functional/query-builder/update/query-builder-update.ts @@ -7,9 +7,11 @@ import {MysqlDriver} from "../../../../src/driver/mysql/MysqlDriver"; import {SqlServerDriver} from "../../../../src/driver/sqlserver/SqlServerDriver"; import {LimitOnUpdateNotSupportedError} from "../../../../src/error/LimitOnUpdateNotSupportedError"; import {Photo} from "./entity/Photo"; +import {EntityColumnNotFound} from "../../../../src/error/EntityColumnNotFound"; +import {UpdateValuesMissingError} from "../../../../src/error/UpdateValuesMissingError"; describe("query builder > update", () => { - + let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], @@ -60,7 +62,7 @@ describe("query builder > update", () => { .where("name = :name", { name: () => connection.driver instanceof SqlServerDriver ? "SUBSTRING('Alex Messer Dimovich', 1, 11)" : "SUBSTR('Alex Messer Dimovich', 1, 11)" }) .execute(); - + const loadedUser1 = await connection.getRepository(User).findOne({ name: "Dima" }); expect(loadedUser1).to.exist; @@ -159,7 +161,7 @@ describe("query builder > update", () => { const user2 = new User(); user2.name = "Muhammad Mirzoev"; const user3 = new User(); - user3.name = "Brad Porter"; + user3.name = "Brad Porter"; await connection.manager.save([user1, user2, user3]); @@ -171,7 +173,7 @@ describe("query builder > update", () => { .update(User) .set({ name: nameToFind }) .limit(limitNum) - .execute(); + .execute(); const loadedUsers = await connection.getRepository(User).find({ name: nameToFind }); expect(loadedUsers).to.exist; @@ -181,8 +183,69 @@ describe("query builder > update", () => { .update(User) .set({ name: nameToFind }) .limit(limitNum) - .execute().should.be.rejectedWith(LimitOnUpdateNotSupportedError); + .execute().should.be.rejectedWith(LimitOnUpdateNotSupportedError); + } + }))); + + it("should throw error when update value is missing", () => Promise.all(connections.map(async connection => { + + const user = new User(); + user.name = "Alex Messer"; + + await connection.manager.save(user); + + let error: Error | undefined; + try { + await connection.createQueryBuilder() + .update(User) + .where("name = :name", { name: "Alex Messer" }) + .execute(); + } catch (err) { + error = err; } + expect(error).to.be.an.instanceof(UpdateValuesMissingError); + + }))); + + it("should throw error when update value is missing 2", () => Promise.all(connections.map(async connection => { + + const user = new User(); + user.name = "Alex Messer"; + + await connection.manager.save(user); + + let error: Error | undefined; + try { + await connection.createQueryBuilder(User, "user") + .update() + .where("name = :name", { name: "Alex Messer" }) + .execute(); + } catch (err) { + error = err; + } + expect(error).to.be.an.instanceof(UpdateValuesMissingError); + + }))); + + it("should throw error when update property in set method is unknown", () => Promise.all(connections.map(async connection => { + + const user = new User(); + user.name = "Alex Messer"; + + await connection.manager.save(user); + + let error: Error | undefined; + try { + await connection.createQueryBuilder() + .update(User) + .set({ unknownProp: true }) + .where("name = :name", { name: "Alex Messer" }) + .execute(); + } catch (err) { + error = err; + } + expect(error).to.be.an.instanceof(EntityColumnNotFound); + }))); -}); \ No newline at end of file +}); diff --git a/test/functional/repository/find-options-operators/repository-find-operators.ts b/test/functional/repository/find-options-operators/repository-find-operators.ts index 88c3fe070a..38bb3113fc 100644 --- a/test/functional/repository/find-options-operators/repository-find-operators.ts +++ b/test/functional/repository/find-options-operators/repository-find-operators.ts @@ -1,6 +1,6 @@ import "reflect-metadata"; import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils"; -import {Any, Between, Connection, Equal, In, IsNull, LessThan, Like, MoreThan, Not} from "../../../../src"; +import {Any, Between, Connection, Equal, In, IsNull, LessThan, LessThanOrEqual, Like, MoreThan, MoreThanOrEqual, Not} from "../../../../src"; import {Post} from "./entity/Post"; import {PostgresDriver} from "../../../../src/driver/postgres/PostgresDriver"; import {Raw} from "../../../../src/find-options/operator/Raw"; @@ -54,6 +54,33 @@ describe("repository > find options > operators", () => { }))); + it("lessThanOrEqual", () => Promise.all(connections.map(async connection => { + + // insert some fake data + const post1 = new Post(); + post1.title = "About #1"; + post1.likes = 12; + await connection.manager.save(post1); + const post2 = new Post(); + post2.title = "About #2"; + post2.likes = 3; + await connection.manager.save(post2); + const post3 = new Post(); + post3.title = "About #3"; + post3.likes = 13; + await connection.manager.save(post3); + + // check operator + const loadedPosts = await connection.getRepository(Post).find({ + likes: LessThanOrEqual(12) + }); + loadedPosts.should.be.eql([ + { id: 1, likes: 12, title: "About #1" }, + { id: 2, likes: 3, title: "About #2" } + ]); + + }))); + it("not(lessThan)", () => Promise.all(connections.map(async connection => { // insert some fake data @@ -74,6 +101,30 @@ describe("repository > find options > operators", () => { }))); + it("not(lessThanOrEqual)", () => Promise.all(connections.map(async connection => { + + // insert some fake data + const post1 = new Post(); + post1.title = "About #1"; + post1.likes = 12; + await connection.manager.save(post1); + const post2 = new Post(); + post2.title = "About #2"; + post2.likes = 3; + await connection.manager.save(post2); + const post3 = new Post(); + post3.title = "About #3"; + post3.likes = 13; + await connection.manager.save(post3); + + // check operator + const loadedPosts = await connection.getRepository(Post).find({ + likes: Not(LessThanOrEqual(12)) + }); + loadedPosts.should.be.eql([{ id: 3, likes: 13, title: "About #3" }]); + + }))); + it("moreThan", () => Promise.all(connections.map(async connection => { // insert some fake data @@ -94,6 +145,33 @@ describe("repository > find options > operators", () => { }))); + it("moreThanOrEqual", () => Promise.all(connections.map(async connection => { + + // insert some fake data + const post1 = new Post(); + post1.title = "About #1"; + post1.likes = 12; + await connection.manager.save(post1); + const post2 = new Post(); + post2.title = "About #2"; + post2.likes = 3; + await connection.manager.save(post2); + const post3 = new Post(); + post3.title = "About #3"; + post3.likes = 13; + await connection.manager.save(post3); + + // check operator + const loadedPosts = await connection.getRepository(Post).find({ + likes: MoreThanOrEqual(12) + }); + loadedPosts.should.be.eql([ + { id: 1, likes: 12, title: "About #1" }, + { id: 3, likes: 13, title: "About #3" } + ]); + + }))); + it("not(moreThan)", () => Promise.all(connections.map(async connection => { // insert some fake data @@ -114,6 +192,30 @@ describe("repository > find options > operators", () => { }))); + it("not(moreThanOrEqual)", () => Promise.all(connections.map(async connection => { + + // insert some fake data + const post1 = new Post(); + post1.title = "About #1"; + post1.likes = 12; + await connection.manager.save(post1); + const post2 = new Post(); + post2.title = "About #2"; + post2.likes = 3; + await connection.manager.save(post2); + const post3 = new Post(); + post3.title = "About #3"; + post3.likes = 13; + await connection.manager.save(post3); + + // check operator + const loadedPosts = await connection.getRepository(Post).find({ + likes: Not(MoreThanOrEqual(12)) + }); + loadedPosts.should.be.eql([{ id: 2, likes: 3, title: "About #2" }]); + + }))); + it("equal", () => Promise.all(connections.map(async connection => { // insert some fake data @@ -417,4 +519,4 @@ describe("repository > find options > operators", () => { }))); -}); \ No newline at end of file +});