Skip to content

Commit

Permalink
Release 4.1.0
Browse files Browse the repository at this point in the history
* Fix issues with `like` and array containing null or empty string. Specifically support negated array type value with null and empty string.
* Fix `like` constraint with null value
  • Loading branch information
jgeurts committed Sep 18, 2020
1 parent b868d56 commit 40a7c8e
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 9 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
### 4.1.0
* Fix issues with `like` and array containing null or empty string.
Specifically support negated array type value with null and empty string.
* Fix `like` constraint with null value

### 4.0.2
* Update npms

Expand Down
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
[![node version](https://img.shields.io/node/v/bigal.svg?style=flat)](https://nodejs.org)
[![Known Vulnerabilities](https://snyk.io/test/npm/bigal/badge.svg)](https://snyk.io/test/npm/bigal)

A fast, lightweight ORM for PostgreSQL and node.js, written in Typescript.
A fast, lightweight ORM for PostgreSQL and node.js, written in Typescript.

This ORM does not:
This ORM does not:

* Create or update db schemas for you
* Handle associations/joins
Expand Down Expand Up @@ -216,6 +216,28 @@ const items = await ProductRepository.find({
});
```
#### Example of an OR statement
```js
const items = await PersonRepository.find().where({
firstName: {
like: ['walter', 'Jess%'],
},
});
```
#### Example of an AND statement
```js
const items = await PersonRepository.find().where({
lastName: {
'!': {
lastName: [null, '', 'Whi%'],
},
},
});
```
#### Sorted query before returning result
```js
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bigal",
"version": "4.0.2",
"version": "4.1.0",
"description": "A fast and lightweight orm for postgres and node.js, written in typescript.",
"main": "index.js",
"types": "index.d.ts",
Expand Down
71 changes: 65 additions & 6 deletions src/SqlHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,15 @@ function _buildWhere<T extends Entity>({
value: null,
params,
}));
} else if (item === '') {
orConstraints.push(_buildWhere({
repositoriesByModelNameLowered,
model,
propertyName,
isNegated,
value: '',
params,
}));
} else {
valueWithoutNull.push(item);
}
Expand Down Expand Up @@ -1045,6 +1054,52 @@ function _buildLikeOperatorStatement<T extends Entity>({
}

if (value.length > 1) {
const orConstraints = [];
const valueWithoutNullOrEmpty = [];
for (const item of value) {
if (_.isNull(item)) {
orConstraints.push(_buildLikeOperatorStatement({
model,
propertyName,
isNegated,
value: null,
params,
}));
} else if (item === '') {
orConstraints.push(_buildLikeOperatorStatement({
model,
propertyName,
isNegated,
value: '',
params,
}));
} else {
valueWithoutNullOrEmpty.push(item);
}
}

if (orConstraints.length) {
if (valueWithoutNullOrEmpty.length) {
orConstraints.push(_buildLikeOperatorStatement({
model,
propertyName,
isNegated,
value: valueWithoutNullOrEmpty,
params,
}));
}

if (orConstraints.length === 1) {
return orConstraints[0];
}

if (isNegated) {
return orConstraints.join(' AND ');
}

return `(${orConstraints.join(' OR ')})`;
}

const lowerValues = (value as string[]).map((val) => val.toLowerCase());

// NOTE: This is doing a case-insensitive pattern match
Expand All @@ -1067,13 +1122,17 @@ function _buildLikeOperatorStatement<T extends Entity>({
value = _.first(value as string[]);
}

if (_.isString(value)) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const column = model.columnsByPropertyName[propertyName];
if (!column) {
throw new Error(`Unable to find property ${propertyName} on model ${model.name}`);
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const column = model.columnsByPropertyName[propertyName];
if (!column) {
throw new Error(`Unable to find property ${propertyName} on model ${model.name}`);
}

if (_.isNull(value)) {
return `"${column.name}" ${isNegated ? 'IS NOT' : 'IS'} NULL`;
}

if (_.isString(value)) {
if (value) {
// NOTE: This is doing a case-insensitive pattern match
params.push(value);
Expand Down
40 changes: 40 additions & 0 deletions tests/sqlHelper.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2110,6 +2110,25 @@ describe('sqlHelper', () => {
],
]);
});
it('should handle like with an array of null, empty string, and single value', () => {
const name = faker.random.uuid();
const {
whereStatement,
params,
} = sqlHelper._buildWhereStatement({
repositoriesByModelNameLowered,
model: repositoriesByModelNameLowered.product.model,
where: {
name: {
like: [null, '', name],
},
},
});

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
whereStatement!.should.equal('WHERE ("name" IS NULL OR "name" = \'\' OR "name" ILIKE $1)');
params.should.deep.equal([name]);
});
it('should handle not like with an array of values', () => {
const name1 = 'TestUpper';
const name2 = faker.random.uuid();
Expand Down Expand Up @@ -2137,6 +2156,27 @@ describe('sqlHelper', () => {
],
]);
});
it('should handle not like with an array of null, empty string, and single value', () => {
const name = faker.random.uuid();
const {
whereStatement,
params,
} = sqlHelper._buildWhereStatement({
repositoriesByModelNameLowered,
model: repositoriesByModelNameLowered.product.model,
where: {
name: {
'!': {
like: [null, '', name],
},
},
},
});

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
whereStatement!.should.equal('WHERE "name" IS NOT NULL AND "name" != \'\' AND "name" NOT ILIKE $1');
params.should.deep.equal([name]);
});
it('should handle like with an empty array', () => {
const {
whereStatement,
Expand Down

0 comments on commit 40a7c8e

Please sign in to comment.