Skip to content
This repository has been archived by the owner on Jun 20, 2022. It is now read-only.

Commit

Permalink
feat(core): search with case insensitiveness (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
feugy committed May 30, 2019
1 parent 3c73612 commit f1c60a1
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 6 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
### 2019-05-24 / 2.0.0
### 2019-06-01 / 2.0.0

* feat(core): **Breaking** remove the undocumented `config` property of `TrailsManager` instances
* feat(core): search with exact match
* feat(core): search with case insensitiveness
* fix(core): do not load config when connection pool is provided
* fix(core): error when migrating with numerical version
* fix(core): `TrailsManager.get()` does not return id
Expand Down
4 changes: 3 additions & 1 deletion packages/trail-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,15 @@ Deletes a trail from the database.

Returns `true` if the record was found and deleted, `false` otherwise.

### `async TrailsManager.search({from, to, who, what, subject, page, pageSize, sort})`
### `async TrailsManager.search({from, to, who, what, subject, page, pageSize, sort, exactMatch})`

Searchs for trails in the database.

The `from` and `to` attributes follow the same rule of the `when` trail attributes and are inclusive.

The `who`, `what` and `subject` attributes must be string and can be used to search inside the `id` attributes of the respective trail attributes.
You can use `exactMatch` (default to `false`) to match only trails which `id` is exactly the searched value.
You can use `caseInsensitive` (default to `false`) to ignore case when matching values.

The `page` and `pageSize` attributes can be used to control pagination. They must be positive numbers. The default pageSize is 25.

Expand Down
9 changes: 5 additions & 4 deletions packages/trail-core/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class TrailsManager {
}
}

async search ({from, to, who, what, subject, page, pageSize, sort, exactMatch = false} = {}) {
async search ({from, to, who, what, subject, page, pageSize, sort, exactMatch = false, caseInsensitive = false} = {}) {
// Validate parameters
if (!from) throw new Error('You must specify a starting date ("from" attribute) when querying trails.')
if (!to) throw new Error('You must specify a ending date ("to" attribute) when querying trails.')
Expand Down Expand Up @@ -89,9 +89,10 @@ class TrailsManager {
("when" >= ${from.toISO()} AND "when" <= ${to.toISO()})
`

if (who) sql.append(SQL` AND who_id LIKE ${exactMatch ? who : '%' + who + '%'}`)
if (what) sql.append(SQL` AND what_id LIKE ${exactMatch ? what : '%' + what + '%'}`)
if (subject) sql.append(SQL` AND subject_id LIKE ${exactMatch ? subject : '%' + subject + '%'}`)
const op = caseInsensitive ? 'ILIKE' : 'LIKE'
if (who) sql.append(SQL([` AND who_id ${op} `])).append(SQL`${exactMatch ? who : '%' + who + '%'}`)
if (what) sql.append(SQL([` AND what_id ${op} `])).append(SQL`${exactMatch ? what : '%' + what + '%'}`)
if (subject) sql.append(SQL([` AND subject_id ${op} `])).append(SQL`${exactMatch ? subject : '%' + subject + '%'}`)

const footer = ` ORDER BY ${sortKey} ${sortAsc ? 'ASC' : 'DESC'} LIMIT ${pageSize} OFFSET ${(page - 1) * pageSize}`
sql.append(SQL([footer]))
Expand Down
28 changes: 28 additions & 0 deletions packages/trail-core/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,34 @@ describe('TrailsManager', () => {
.to.reject(Error, 'Only "id", "when", "who", "what" and "subject" are supported for sorting.')
})

test('should return the records with case insensitiveness', async () => {
await this.subject.performDatabaseOperations(client => client.query('TRUNCATE trails'))

const records = [
{when: '2018-01-01T12:34:56+00:00', who: 'dog cat fish', what: 'open MORNing', subject: 'window'},
{when: '2018-01-02T12:34:56+00:00', who: 'dog cat shark', what: 'evening', subject: 'window'},
{when: '2018-01-03T12:34:56+00:00', who: 'wolf cat whale', what: 'open morning', subject: 'door'},
{when: '2018-01-04T12:34:56+00:00', who: 'hyena lion fish', what: 'evening', subject: 'DOOr'},
{when: '2018-01-05T12:34:56+00:00', who: 'hyena tiger whal', what: 'close night', subject: 'world'}
]

const ids = await Promise.all(records.map(r => this.subject.insert(r)))

expect((await this.subject.search({from: '2018-01-01T15:00:00+00:00', to: '2018-01-04T13:34:56+00:00', subject: 'DOOr', sort: 'when', exactMatch: true, caseInsensitive: true}))
.map(r => r.id)).to.equal([ids[2], ids[3]])

expect((await this.subject.search({from: '2018-01-01T15:00:00+00:00', to: '2018-01-04T13:34:56+00:00', subject: 'DOOr', sort: 'when', exactMatch: true}))
.map(r => r.id)).to.equal([ids[3]])

expect((await this.subject.search({from: '2018-01-01T12:00:00+00:00', to: '2018-01-05T13:34:56+00:00', what: 'MORNing', sort: 'when', caseInsensitive: true}))
.map(r => r.id)).to.equal([ids[0], ids[2]])

expect((await this.subject.search({from: '2018-01-01T12:00:00+00:00', to: '2018-01-05T13:34:56+00:00', what: 'MORNing', sort: 'when'}))
.map(r => r.id)).to.equal([ids[0]])

await Promise.all(ids.map(i => this.subject.delete(i)))
})

test('should sanitize pagination parameters', async () => {
const spy = sinon.spy(this.subject, 'performDatabaseOperations')

Expand Down

0 comments on commit f1c60a1

Please sign in to comment.