Compare commits

..

3 commits

Author SHA1 Message Date
Ava Gaiety Wroten
74bfa76a4e Update package.json 2020-09-21 23:27:13 +00:00
Jo Wroten
4c31065249 Add LICENSE 2019-08-07 03:33:03 +00:00
sharpshark28
195121070f 1.1.3 2018-02-22 19:28:53 -06:00
5 changed files with 133 additions and 271 deletions

24
LICENSE Normal file
View 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>

View file

@ -2,38 +2,34 @@
[![Build Status](https://travis-ci.org/sharpshark28/json-query-chain.svg?branch=master)](https://travis-ci.org/sharpshark28/json-query-chain) [![npm version](https://badge.fury.io/js/json-query-chain.svg)](https://badge.fury.io/js/json-query-chain) ![Code Coverage](coverage.svg) [![Maintainability](https://api.codeclimate.com/v1/badges/4dc20d8b5e6a7334044d/maintainability)](https://codeclimate.com/github/sharpshark28/json-query-chain/maintainability) [![Build Status](https://travis-ci.org/sharpshark28/json-query-chain.svg?branch=master)](https://travis-ci.org/sharpshark28/json-query-chain) [![npm version](https://badge.fury.io/js/json-query-chain.svg)](https://badge.fury.io/js/json-query-chain) ![Code Coverage](coverage.svg) [![Maintainability](https://api.codeclimate.com/v1/badges/4dc20d8b5e6a7334044d/maintainability)](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)

View file

@ -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':
let regFind = new RegExp(term, 'gi') this.data = this.data.filter(item => {
let getMatches = item => { let regFind = new RegExp(term, 'gi')
if (typeof item === 'string') { let termMatches = (item[key].match(regFind) || []).length
return (item.match(regFind) || []).length item.sortScore += termMatches
} else { return termMatches
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 if (a[key] < b[key]) return -1
} if (a[key] > b[key]) return 1
return 0
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
return 0
})
} else console.warn('Attempted to use sortBy with a flat array. Try .sort(func) instead.')
return this return this
} }

View file

@ -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,233 +8,102 @@ 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(TestData)
let query = new Query(arrayOf100Things) .paginate()
.paginate() .results
.results
expect(query.length).toBe(10) expect(query.length).toBe(9)
})
test('using default params', () => {
let query = new Query(TestData)
.paginate()
.results
expect(query.length).toBe(9)
})
test('with custom page length, first page', () => {
let query = new Query(TestData)
.paginate(1, 3)
.results
expect(query.length).toBe(3)
expect(query[0].name).toBe('Haynes Meadows')
})
test('with custom page length, second page', () => {
let query = new Query(TestData)
.paginate(2, 3)
.results
expect(query.length).toBe(3)
expect(query[0].name).toBe('Howard Buckley')
})
}) })
describe('.search()', () => { test('should paginate with custom page length', () => {
test('partial string in flat array of strings', () => { let query = new Query(TestData)
let query = new Query(['bar', 'foo', 'foobar', 'foofoobar']) .paginate(1, 3)
.search('foo') .results
.results
expect(query[0]).toBe('foofoobar') expect(query.length).toBe(3)
expect(query).toContain('foo') expect(query[0].name).toBe('Haynes Meadows')
expect(query).not.toContain('bar')
})
test('by name with value/key', () => {
let query = new Query(TestData)
.search('steele', 'name')
.results
expect(query.length).toBe(2)
})
test('by boolean isActive', () => {
let query = new Query(TestData)
.search(true, 'isActive')
.results
expect(query.length).toBe(4)
})
test('by boolean isActive, false', () => {
let query = new Query(TestData)
.search(false, 'isActive')
.results
expect(query.length).toBe(5)
})
test('warn when searching a flat array of booleans', () => {
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('should paginate to second page with custom page length', () => {
test('with flat array', () => { let query = new Query(TestData)
let alphabetical = (a, b) => a > b .paginate(2, 3)
.results
let query = new Query(['foo', 'bar', 'foobar']) expect(query.length).toBe(3)
.sort(alphabetical) expect(query[0].name).toBe('Howard Buckley')
.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)
.sort(alphabetical)
.results
expect(query[0].name).toBe('Dudley Conner')
expect(query[query.length - 1].name).toBe('Wade Steele')
})
}) })
describe('.sortBy()', () => { test('should search by boolean isActive', () => {
test('by boolean', () => { let query = new Query(TestData)
let query = new Query(TestData) .search('isActive', true)
.sortBy('isActive') .results
.results
expect(query[0].name).toBe('Katelyn Steele') expect(query.length).toBe(4)
})
test('by number', () => {
let query = new Query(TestData)
.sortBy('netWorth')
.results
expect(query[0].name).toBe('Howard Buckley') // Negative
expect(query[1].name).toBe('Natalia Petty') // 0
expect(query[query.length - 1].name).toBe('Newman Mays') // Richest
})
test('by string', () => {
let query = new Query(TestData)
.sortBy('name')
.results
expect(query[0].name).toBe('Dudley Conner')
})
test('warn when using sortBy with a flat array', () => {
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('should search by name', () => {
test('with a flat array', () => { let query = new Query(TestData)
let isEven = a => !(a % 2) .search('name', 'steele')
.results
let query = new Query(arrayOf100Things) expect(query.length).toBe(2)
.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 query = new Query(TestData)
.filter(isAgeOver33)
.results
expect(query[0].name).toBe('Howard Buckley')
})
}) })
describe('.filterBy()', () => { test('should sort by boolean isActive', () => {
test('by key', () => { let query = new Query(TestData)
let isNumGT33 = num => num > 33 .sort('isActive')
.results
let query = new Query(TestData) expect(query[0].name).toBe('Katelyn Steele')
.filterBy('age', isNumGT33)
.results
expect(query[0].name).toBe('Howard Buckley')
})
test('warn when using filterBy with a flat array', () => {
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('should sort by number netWorth', () => {
test('with a flat array', () => { let query = new Query(TestData)
let query = new Query(['bar', 'foo', 'foobar', 'foofoobar']) .sort('netWorth')
.search('foo') .results
.sort()
.paginate(1, 2)
.results
expect(query.length).toBe(2) expect(query[0].name).toBe('Howard Buckley') // Negative
expect(query[0]).toBe('foo') expect(query[1].name).toBe('Natalia Petty') // 0
expect(query[1]).toBe('foobar') expect(query[query.length - 1].name).toBe('Newman Mays') // Richest
}) })
test('with an array of objects', () => { test('should sort by string name', () => {
let query = new Query(TestData) let query = new Query(TestData)
.search(true, 'isActive') .sort('name')
.sortBy('name') .results
.paginate(1, 2)
.results expect(query[0].name).toBe('Dudley Conner')
})
expect(query.length).toBe(2)
expect(query[0].name).toBe('Dudley Conner') test('should filter', () => {
expect(query[query.length - 1].name).toBe('Haynes Meadows') let isAgeOver33 = a => a.age > 33
})
let query = new Query(TestData)
.filter(isAgeOver33)
.results
expect(query[0].name).toBe('Howard Buckley')
})
test('should filter by key', () => {
let isNumGT33 = num => num > 33
let query = new Query(TestData)
.filterBy('age', isNumGT33)
.results
expect(query[0].name).toBe('Howard Buckley')
})
test('should chain everything together', () => {
let query = new Query(TestData)
.search('isActive', true)
.sort('name')
.paginate(1, 2)
.results
expect(query.length).toBe(2)
expect(query[0].name).toBe('Dudley Conner')
expect(query[query.length - 1].name).toBe('Haynes Meadows')
}) })

View file

@ -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"