Compare commits
3 commits
collection
...
main
Author | SHA1 | Date | |
---|---|---|---|
![]() |
74bfa76a4e | ||
![]() |
4c31065249 | ||
![]() |
195121070f |
5 changed files with 133 additions and 271 deletions
24
LICENSE
Normal file
24
LICENSE
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to <https://unlicense.org>
|
38
README.md
38
README.md
|
@ -2,38 +2,34 @@
|
||||||
|
|
||||||
[](https://travis-ci.org/sharpshark28/json-query-chain) [](https://badge.fury.io/js/json-query-chain)  [](https://codeclimate.com/github/sharpshark28/json-query-chain/maintainability)
|
[](https://travis-ci.org/sharpshark28/json-query-chain) [](https://badge.fury.io/js/json-query-chain)  [](https://codeclimate.com/github/sharpshark28/json-query-chain/maintainability)
|
||||||
|
|
||||||
Chain queries onto arrays and arrays of objects to return precise results. See [example usages in the tests](./index.test.js) running on [this test data](./testdata.json).
|
Chain queries onto POJOs to return precise results.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import Query from 'json-query-chain';
|
import Query from 'json-query-chain';
|
||||||
|
|
||||||
let query = new Query(someJsonData)
|
let myQ = new Query(someJsonData)
|
||||||
.search('isActiveUser', true)
|
.search('isActiveUser', true)
|
||||||
.results;
|
.results;
|
||||||
|
|
||||||
console.log('Results: ', query);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Chainable Methods
|
### Chainable Methods
|
||||||
|
|
||||||
#### Search
|
#### Search
|
||||||
|
|
||||||
|
Currently supports booleans and strings. (See [#1](https://github.com/sharpshark28/json-query-chain/issues/1) for Integer Support)
|
||||||
|
|
||||||
##### By Boolean
|
##### By Boolean
|
||||||
|
|
||||||
Acts as a smart filter returning only elements who's key matches the expected result.
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
.search(true, 'isActiveUser')
|
.search('isActiveUser', true)
|
||||||
```
|
```
|
||||||
|
|
||||||
##### By String
|
##### By String
|
||||||
|
|
||||||
Sorts and filters out any elements in the array not matching the requested value while attempting to raise the best results to the top (most frequent number of occurrences).
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
.search('steele', 'name')
|
.search('name', 'steele')
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Filter
|
#### Filter
|
||||||
|
@ -46,42 +42,28 @@ Simpler version of search using a custom function in the chain.
|
||||||
|
|
||||||
##### By Key
|
##### By Key
|
||||||
|
|
||||||
Like `.filter()`, but narrowed down by key.
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
.filterBy('age', x => x >= 21)
|
.filterBy('age', x => x >= 21)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Sort
|
#### Sort
|
||||||
|
|
||||||
A chainable version of javascript's built in array sort.
|
|
||||||
|
|
||||||
```
|
|
||||||
.sort((a, b) => a > b)
|
|
||||||
```
|
|
||||||
|
|
||||||
##### By Boolean
|
##### By Boolean
|
||||||
|
|
||||||
Abstracted sort by matching a key to a boolean.
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
.sortBy('isActiveUser')
|
.sort('isActiveUser', true)
|
||||||
```
|
```
|
||||||
|
|
||||||
##### By String
|
##### By String
|
||||||
|
|
||||||
Sorts alphabetically based on key.
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
.sortBy('name')
|
.sort('name')
|
||||||
```
|
```
|
||||||
|
|
||||||
##### By Number
|
##### By Number
|
||||||
|
|
||||||
Sorts by ascending numerical order based on key.
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
.sortBy('netWorth')
|
.sort('netWorth')
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Pagination
|
#### Pagination
|
||||||
|
@ -92,7 +74,7 @@ Page 1 with 5 results per page.
|
||||||
.paginate(1, 5)
|
.paginate(1, 5)
|
||||||
```
|
```
|
||||||
|
|
||||||
Page 2 with default of 10 results per page.
|
Page 2 wtih default of 10 results per page.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
.paginate(2)
|
.paginate(2)
|
||||||
|
|
35
index.js
35
index.js
|
@ -1,6 +1,9 @@
|
||||||
module.exports = class Query {
|
module.exports = class Query {
|
||||||
constructor (data) {
|
constructor (data) {
|
||||||
this.data = data
|
this.data = data.map(item => {
|
||||||
|
item.sortScore = 0
|
||||||
|
return item
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
get results () {
|
get results () {
|
||||||
|
@ -13,47 +16,33 @@ module.exports = class Query {
|
||||||
}
|
}
|
||||||
|
|
||||||
filterBy (key, func) {
|
filterBy (key, func) {
|
||||||
if (this.data.length && typeof this.data[0] === 'object') {
|
|
||||||
this.data = this.data.filter(item => func(item[key]))
|
this.data = this.data.filter(item => func(item[key]))
|
||||||
} else console.warn('Attempted to use filterBy with a flat array')
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
search (term, key) {
|
search (key, term, score = 0) {
|
||||||
switch (typeof term) {
|
switch (typeof term) {
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
if (this.data.length && typeof this.data[0] === 'object') {
|
|
||||||
this.data = this.data.filter(item => item[key] === term)
|
this.data = this.data.filter(item => item[key] === term)
|
||||||
} else console.warn('Attempted to use search by boolean with a flat array')
|
|
||||||
break
|
break
|
||||||
case 'string':
|
case 'string':
|
||||||
|
this.data = this.data.filter(item => {
|
||||||
let regFind = new RegExp(term, 'gi')
|
let regFind = new RegExp(term, 'gi')
|
||||||
let getMatches = item => {
|
let termMatches = (item[key].match(regFind) || []).length
|
||||||
if (typeof item === 'string') {
|
item.sortScore += termMatches
|
||||||
return (item.match(regFind) || []).length
|
return termMatches
|
||||||
} else {
|
})
|
||||||
return (item[key].match(regFind) || []).length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.data = [...this.data].sort(getMatches).filter(getMatches)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
sort (func) {
|
sort (key = 'sortScore') {
|
||||||
this.data = [...this.data].sort(func)
|
this.data = this.data.sort((a, b) => {
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
sortBy (key) {
|
|
||||||
if (this.data.length && typeof this.data[0] === 'object') {
|
|
||||||
this.data = [...this.data].sort((a, b) => {
|
|
||||||
if (a[key] < b[key]) return -1
|
if (a[key] < b[key]) return -1
|
||||||
if (a[key] > b[key]) return 1
|
if (a[key] > b[key]) return 1
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
} else console.warn('Attempted to use sortBy with a flat array. Try .sort(func) instead.')
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
171
index.test.js
171
index.test.js
|
@ -1,8 +1,6 @@
|
||||||
const Query = require('./index')
|
const Query = require('./index')
|
||||||
const TestData = require('./testdata.json')
|
const TestData = require('./testdata.json')
|
||||||
|
|
||||||
const arrayOf100Things = [...Array(100).keys()]
|
|
||||||
|
|
||||||
test('should not modify passed data without chain alterations', () => {
|
test('should not modify passed data without chain alterations', () => {
|
||||||
let query = new Query(TestData)
|
let query = new Query(TestData)
|
||||||
.results
|
.results
|
||||||
|
@ -10,16 +8,7 @@ test('should not modify passed data without chain alterations', () => {
|
||||||
expect(query).toMatchObject(TestData)
|
expect(query).toMatchObject(TestData)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.paginate()', () => {
|
test('should paginate with default params', () => {
|
||||||
test('with a flat array', () => {
|
|
||||||
let query = new Query(arrayOf100Things)
|
|
||||||
.paginate()
|
|
||||||
.results
|
|
||||||
|
|
||||||
expect(query.length).toBe(10)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('using default params', () => {
|
|
||||||
let query = new Query(TestData)
|
let query = new Query(TestData)
|
||||||
.paginate()
|
.paginate()
|
||||||
.results
|
.results
|
||||||
|
@ -27,7 +16,7 @@ describe('.paginate()', () => {
|
||||||
expect(query.length).toBe(9)
|
expect(query.length).toBe(9)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('with custom page length, first page', () => {
|
test('should paginate with custom page length', () => {
|
||||||
let query = new Query(TestData)
|
let query = new Query(TestData)
|
||||||
.paginate(1, 3)
|
.paginate(1, 3)
|
||||||
.results
|
.results
|
||||||
|
@ -36,7 +25,7 @@ describe('.paginate()', () => {
|
||||||
expect(query[0].name).toBe('Haynes Meadows')
|
expect(query[0].name).toBe('Haynes Meadows')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('with custom page length, second page', () => {
|
test('should paginate to second page with custom page length', () => {
|
||||||
let query = new Query(TestData)
|
let query = new Query(TestData)
|
||||||
.paginate(2, 3)
|
.paginate(2, 3)
|
||||||
.results
|
.results
|
||||||
|
@ -44,96 +33,34 @@ describe('.paginate()', () => {
|
||||||
expect(query.length).toBe(3)
|
expect(query.length).toBe(3)
|
||||||
expect(query[0].name).toBe('Howard Buckley')
|
expect(query[0].name).toBe('Howard Buckley')
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
describe('.search()', () => {
|
test('should search by boolean isActive', () => {
|
||||||
test('partial string in flat array of strings', () => {
|
|
||||||
let query = new Query(['bar', 'foo', 'foobar', 'foofoobar'])
|
|
||||||
.search('foo')
|
|
||||||
.results
|
|
||||||
|
|
||||||
expect(query[0]).toBe('foofoobar')
|
|
||||||
expect(query).toContain('foo')
|
|
||||||
expect(query).not.toContain('bar')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('by name with value/key', () => {
|
|
||||||
let query = new Query(TestData)
|
let query = new Query(TestData)
|
||||||
.search('steele', 'name')
|
.search('isActive', true)
|
||||||
.results
|
|
||||||
|
|
||||||
expect(query.length).toBe(2)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('by boolean isActive', () => {
|
|
||||||
let query = new Query(TestData)
|
|
||||||
.search(true, 'isActive')
|
|
||||||
.results
|
.results
|
||||||
|
|
||||||
expect(query.length).toBe(4)
|
expect(query.length).toBe(4)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('by boolean isActive, false', () => {
|
test('should search by name', () => {
|
||||||
let query = new Query(TestData)
|
let query = new Query(TestData)
|
||||||
.search(false, 'isActive')
|
.search('name', 'steele')
|
||||||
.results
|
.results
|
||||||
|
|
||||||
expect(query.length).toBe(5)
|
expect(query.length).toBe(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('warn when searching a flat array of booleans', () => {
|
test('should sort by boolean isActive', () => {
|
||||||
let consoleWarnSpy = jest.spyOn(global.console, 'warn')
|
|
||||||
consoleWarnSpy.mockImplementation(() => {})
|
|
||||||
|
|
||||||
let query = new Query(arrayOf100Things)
|
|
||||||
.search(true, 'N/A')
|
|
||||||
.results
|
|
||||||
|
|
||||||
expect(query).toEqual(arrayOf100Things)
|
|
||||||
expect(consoleWarnSpy).toHaveBeenCalled()
|
|
||||||
|
|
||||||
consoleWarnSpy.mockReset()
|
|
||||||
consoleWarnSpy.mockRestore()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('.sort()', () => {
|
|
||||||
test('with flat array', () => {
|
|
||||||
let alphabetical = (a, b) => a > b
|
|
||||||
|
|
||||||
let query = new Query(['foo', 'bar', 'foobar'])
|
|
||||||
.sort(alphabetical)
|
|
||||||
.results
|
|
||||||
|
|
||||||
expect(query[0]).toBe('bar')
|
|
||||||
expect(query[1]).toBe('foo')
|
|
||||||
expect(query[2]).toBe('foobar')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('with an array of objects', () => {
|
|
||||||
let alphabetical = (a, b) => a.name > b.name
|
|
||||||
|
|
||||||
let query = new Query(TestData)
|
let query = new Query(TestData)
|
||||||
.sort(alphabetical)
|
.sort('isActive')
|
||||||
.results
|
|
||||||
|
|
||||||
expect(query[0].name).toBe('Dudley Conner')
|
|
||||||
expect(query[query.length - 1].name).toBe('Wade Steele')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('.sortBy()', () => {
|
|
||||||
test('by boolean', () => {
|
|
||||||
let query = new Query(TestData)
|
|
||||||
.sortBy('isActive')
|
|
||||||
.results
|
.results
|
||||||
|
|
||||||
expect(query[0].name).toBe('Katelyn Steele')
|
expect(query[0].name).toBe('Katelyn Steele')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('by number', () => {
|
test('should sort by number netWorth', () => {
|
||||||
let query = new Query(TestData)
|
let query = new Query(TestData)
|
||||||
.sortBy('netWorth')
|
.sort('netWorth')
|
||||||
.results
|
.results
|
||||||
|
|
||||||
expect(query[0].name).toBe('Howard Buckley') // Negative
|
expect(query[0].name).toBe('Howard Buckley') // Negative
|
||||||
|
@ -141,43 +68,15 @@ describe('.sortBy()', () => {
|
||||||
expect(query[query.length - 1].name).toBe('Newman Mays') // Richest
|
expect(query[query.length - 1].name).toBe('Newman Mays') // Richest
|
||||||
})
|
})
|
||||||
|
|
||||||
test('by string', () => {
|
test('should sort by string name', () => {
|
||||||
let query = new Query(TestData)
|
let query = new Query(TestData)
|
||||||
.sortBy('name')
|
.sort('name')
|
||||||
.results
|
.results
|
||||||
|
|
||||||
expect(query[0].name).toBe('Dudley Conner')
|
expect(query[0].name).toBe('Dudley Conner')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('warn when using sortBy with a flat array', () => {
|
test('should filter', () => {
|
||||||
let consoleWarnSpy = jest.spyOn(global.console, 'warn')
|
|
||||||
consoleWarnSpy.mockImplementation(() => {})
|
|
||||||
|
|
||||||
let query = new Query(arrayOf100Things)
|
|
||||||
.sortBy('N/A')
|
|
||||||
.results
|
|
||||||
|
|
||||||
expect(query).toEqual(arrayOf100Things)
|
|
||||||
expect(consoleWarnSpy).toHaveBeenCalled()
|
|
||||||
|
|
||||||
consoleWarnSpy.mockReset()
|
|
||||||
consoleWarnSpy.mockRestore()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('.filter()', () => {
|
|
||||||
test('with a flat array', () => {
|
|
||||||
let isEven = a => !(a % 2)
|
|
||||||
|
|
||||||
let query = new Query(arrayOf100Things)
|
|
||||||
.filter(isEven)
|
|
||||||
.results
|
|
||||||
|
|
||||||
expect(query).toContain(2)
|
|
||||||
expect(query).not.toContain(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('with an array of objects', () => {
|
|
||||||
let isAgeOver33 = a => a.age > 33
|
let isAgeOver33 = a => a.age > 33
|
||||||
|
|
||||||
let query = new Query(TestData)
|
let query = new Query(TestData)
|
||||||
|
@ -186,10 +85,8 @@ describe('.filter()', () => {
|
||||||
|
|
||||||
expect(query[0].name).toBe('Howard Buckley')
|
expect(query[0].name).toBe('Howard Buckley')
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
describe('.filterBy()', () => {
|
test('should filter by key', () => {
|
||||||
test('by key', () => {
|
|
||||||
let isNumGT33 = num => num > 33
|
let isNumGT33 = num => num > 33
|
||||||
|
|
||||||
let query = new Query(TestData)
|
let query = new Query(TestData)
|
||||||
|
@ -199,39 +96,10 @@ describe('.filterBy()', () => {
|
||||||
expect(query[0].name).toBe('Howard Buckley')
|
expect(query[0].name).toBe('Howard Buckley')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('warn when using filterBy with a flat array', () => {
|
test('should chain everything together', () => {
|
||||||
let consoleWarnSpy = jest.spyOn(global.console, 'warn')
|
|
||||||
consoleWarnSpy.mockImplementation(() => {})
|
|
||||||
|
|
||||||
let query = new Query(arrayOf100Things)
|
|
||||||
.filterBy('N/A', () => {})
|
|
||||||
.results
|
|
||||||
|
|
||||||
expect(query).toEqual(arrayOf100Things)
|
|
||||||
expect(consoleWarnSpy).toHaveBeenCalled()
|
|
||||||
|
|
||||||
consoleWarnSpy.mockReset()
|
|
||||||
consoleWarnSpy.mockRestore()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('chaining everything together', () => {
|
|
||||||
test('with a flat array', () => {
|
|
||||||
let query = new Query(['bar', 'foo', 'foobar', 'foofoobar'])
|
|
||||||
.search('foo')
|
|
||||||
.sort()
|
|
||||||
.paginate(1, 2)
|
|
||||||
.results
|
|
||||||
|
|
||||||
expect(query.length).toBe(2)
|
|
||||||
expect(query[0]).toBe('foo')
|
|
||||||
expect(query[1]).toBe('foobar')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('with an array of objects', () => {
|
|
||||||
let query = new Query(TestData)
|
let query = new Query(TestData)
|
||||||
.search(true, 'isActive')
|
.search('isActive', true)
|
||||||
.sortBy('name')
|
.sort('name')
|
||||||
.paginate(1, 2)
|
.paginate(1, 2)
|
||||||
.results
|
.results
|
||||||
|
|
||||||
|
@ -239,4 +107,3 @@ describe('chaining everything together', () => {
|
||||||
expect(query[0].name).toBe('Dudley Conner')
|
expect(query[0].name).toBe('Dudley Conner')
|
||||||
expect(query[query.length - 1].name).toBe('Haynes Meadows')
|
expect(query[query.length - 1].name).toBe('Haynes Meadows')
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+ssh://git@github.com/sharpshark28/json-query-chain.git"
|
"url": "git+ssh://git@github.com/sharpshark28/json-query-chain.git"
|
||||||
},
|
},
|
||||||
"author": "Joe Wroten <joe@wroten.me>",
|
"author": "Ava Wroten <ava@wroten.me>",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/sharpshark28/json-query-chain/issues"
|
"url": "https://github.com/sharpshark28/json-query-chain/issues"
|
||||||
|
|
Loading…
Add table
Reference in a new issue