Skip to content

Commit

Permalink
feat: new array find operators (#8766)
Browse files Browse the repository at this point in the history
* added new array find operators; delegating how json and jsonb are stored directly to underlying driver (breaking)

* array: true must be explicitly defined for array json/jsonb values; strings being JSON-stringified must be manually escpaed
  • Loading branch information
pleerock committed Mar 21, 2022
1 parent d61f857 commit 9f1b8e3
Show file tree
Hide file tree
Showing 26 changed files with 938 additions and 27 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Expand Up @@ -426,6 +426,13 @@ This change was required to simplify ORM internals and introduce new features.

* issue with non-reliable `new Date(ISOString)` parsing ([#7796](https://github.com/typeorm/typeorm/pull/7796))

* true JSON / JSONB support - manual `JSON.stringify` was removed,
instead object handled by underlying driver. This opens ability to properly work with json/jsonb structures,
but brings few breaking changes:

* `array: true` must be explicitly defined for array json/jsonb values
* strings being JSON-stringified must be manually escaped

### DEPRECATIONS

* all CLI commands do not support `ormconfig` anymore. You must specify a file with data source instance instead.
Expand Down
48 changes: 48 additions & 0 deletions docs/find-options.md
Expand Up @@ -488,6 +488,54 @@ will execute following query:
SELECT * FROM "post" WHERE "title" IS NULL
```

- `ArrayContains`

```ts
import { ArrayContains } from "typeorm"

const loadedPosts = await dataSource.getRepository(Post).findBy({
categories: ArrayContains(["TypeScript"]),
})
```

will execute following query:

```sql
SELECT * FROM "post" WHERE "categories" @> '{TypeScript}'
```

- `ArrayContainedBy`

```ts
import { ArrayContainedBy } from "typeorm"

const loadedPosts = await dataSource.getRepository(Post).findBy({
categories: ArrayContainedBy(["TypeScript"]),
})
```

will execute following query:

```sql
SELECT * FROM "post" WHERE "categories" <@ '{TypeScript}'
```

- `ArrayOverlap`

```ts
import { ArrayOverlap } from "typeorm"

const loadedPosts = await dataSource.getRepository(Post).findBy({
categories: ArrayOverlap(["TypeScript"]),
})
```

will execute following query:

```sql
SELECT * FROM "post" WHERE "categories" && '{TypeScript}'
```

- `Raw`

```ts
Expand Down
2 changes: 1 addition & 1 deletion src/driver/postgres/PostgresDriver.ts
Expand Up @@ -622,7 +622,7 @@ export class PostgresDriver implements Driver {
columnMetadata.type,
) >= 0
) {
return JSON.stringify(value)
return value // JSON.stringify()
} else if (columnMetadata.type === "hstore") {
if (typeof value === "string") {
return value
Expand Down
3 changes: 3 additions & 0 deletions src/find-options/FindOperatorType.ts
Expand Up @@ -15,3 +15,6 @@ export type FindOperatorType =
| "ilike"
| "like"
| "raw"
| "arrayContains"
| "arrayContainedBy"
| "arrayOverlap"
11 changes: 11 additions & 0 deletions src/find-options/operator/ArrayContainedBy.ts
@@ -0,0 +1,11 @@
import { FindOperator } from "../FindOperator"

/**
* FindOptions Operator.
* Example: { someField: ArrayContainedBy([...]) }
*/
export function ArrayContainedBy<T>(
value: T[] | FindOperator<T>,
): FindOperator<any> {
return new FindOperator("arrayContainedBy", value as any)
}
11 changes: 11 additions & 0 deletions src/find-options/operator/ArrayContains.ts
@@ -0,0 +1,11 @@
import { FindOperator } from "../FindOperator"

/**
* FindOptions Operator.
* Example: { someField: ArrayContains([...]) }
*/
export function ArrayContains<T>(
value: T[] | FindOperator<T>,
): FindOperator<any> {
return new FindOperator("arrayContains", value as any)
}
11 changes: 11 additions & 0 deletions src/find-options/operator/ArrayOverlap.ts
@@ -0,0 +1,11 @@
import { FindOperator } from "../FindOperator"

/**
* FindOptions Operator.
* Example: { someField: ArrayOverlap([...]) }
*/
export function ArrayOverlap<T>(
value: T[] | FindOperator<T>,
): FindOperator<any> {
return new FindOperator("arrayOverlap", value as any)
}
6 changes: 6 additions & 0 deletions src/query-builder/QueryBuilder.ts
Expand Up @@ -993,6 +993,12 @@ export abstract class QueryBuilder<Entity> {
return `${condition.parameters[0]} < ${condition.parameters[1]}`
case "lessThanOrEqual":
return `${condition.parameters[0]} <= ${condition.parameters[1]}`
case "arrayContains":
return `${condition.parameters[0]} @> ${condition.parameters[1]}`
case "arrayContainedBy":
return `${condition.parameters[0]} <@ ${condition.parameters[1]}`
case "arrayOverlap":
return `${condition.parameters[0]} && ${condition.parameters[1]}`
case "moreThan":
return `${condition.parameters[0]} > ${condition.parameters[1]}`
case "moreThanOrEqual":
Expand Down
3 changes: 3 additions & 0 deletions src/query-builder/WhereClause.ts
Expand Up @@ -13,6 +13,9 @@ type PredicateOperator =
| "in"
| "any"
| "isNull"
| "arrayContains"
| "arrayContainedBy"
| "arrayOverlap"

export interface WherePredicateOperator {
operator: PredicateOperator
Expand Down
2 changes: 1 addition & 1 deletion test/benchmark/bulk-save-case2/entity/Document.ts
Expand Up @@ -16,7 +16,7 @@ export class Document {
@Column("text")
context: string

@Column({ type: "jsonb" })
@Column({ type: "jsonb", array: true })
distributions: Distribution[]

@Column({ type: "timestamp with time zone" })
Expand Down

0 comments on commit 9f1b8e3

Please sign in to comment.