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)
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
```javascript
import Query from 'json-query-chain';
let query = new Query(someJsonData)
let myQ = new Query(someJsonData)
.search('isActiveUser', true)
.results;
console.log('Results: ', query);
```
### Chainable Methods
#### Search
Currently supports booleans and strings. (See [#1](https://github.com/sharpshark28/json-query-chain/issues/1) for Integer Support)
##### By Boolean
Acts as a smart filter returning only elements who's key matches the expected result.
```javascript
.search(true, 'isActiveUser')
.search('isActiveUser', true)
```
##### 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
.search('steele', 'name')
.search('name', 'steele')
```
#### Filter
@ -46,42 +42,28 @@ Simpler version of search using a custom function in the chain.
##### By Key
Like `.filter()`, but narrowed down by key.
```javascript
.filterBy('age', x => x >= 21)
```
#### Sort
A chainable version of javascript's built in array sort.
```
.sort((a, b) => a > b)
```
##### By Boolean
Abstracted sort by matching a key to a boolean.
```javascript
.sortBy('isActiveUser')
.sort('isActiveUser', true)
```
##### By String
Sorts alphabetically based on key.
```javascript
.sortBy('name')
.sort('name')
```
##### By Number
Sorts by ascending numerical order based on key.
```javascript
.sortBy('netWorth')
.sort('netWorth')
```
#### Pagination
@ -92,7 +74,7 @@ Page 1 with 5 results per page.
.paginate(1, 5)
```
Page 2 with default of 10 results per page.
Page 2 wtih default of 10 results per page.
```javascript
.paginate(2)

View file

@ -1,6 +1,9 @@
module.exports = class Query {
constructor (data) {
this.data = data
this.data = data.map(item => {
item.sortScore = 0
return item
})
}
get results () {
@ -13,47 +16,33 @@ module.exports = class Query {
}
filterBy (key, func) {
if (this.data.length && typeof this.data[0] === 'object') {
this.data = this.data.filter(item => func(item[key]))
} else console.warn('Attempted to use filterBy with a flat array')
this.data = this.data.filter(item => func(item[key]))
return this
}
search (term, key) {
search (key, term, score = 0) {
switch (typeof term) {
case 'boolean':
if (this.data.length && typeof this.data[0] === 'object') {
this.data = this.data.filter(item => item[key] === term)
} else console.warn('Attempted to use search by boolean with a flat array')
this.data = this.data.filter(item => item[key] === term)
break
case 'string':
let regFind = new RegExp(term, 'gi')
let getMatches = item => {
if (typeof item === 'string') {
return (item.match(regFind) || []).length
} else {
return (item[key].match(regFind) || []).length
}
}
this.data = [...this.data].sort(getMatches).filter(getMatches)
this.data = this.data.filter(item => {
let regFind = new RegExp(term, 'gi')
let termMatches = (item[key].match(regFind) || []).length
item.sortScore += termMatches
return termMatches
})
break
}
return this
}
sort (func) {
this.data = [...this.data].sort(func)
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
return 0
})
} else console.warn('Attempted to use sortBy with a flat array. Try .sort(func) instead.')
sort (key = 'sortScore') {
this.data = this.data.sort((a, b) => {
if (a[key] < b[key]) return -1
if (a[key] > b[key]) return 1
return 0
})
return this
}

View file

@ -1,8 +1,6 @@
const Query = require('./index')
const TestData = require('./testdata.json')
const arrayOf100Things = [...Array(100).keys()]
test('should not modify passed data without chain alterations', () => {
let query = new Query(TestData)
.results
@ -10,233 +8,102 @@ test('should not modify passed data without chain alterations', () => {
expect(query).toMatchObject(TestData)
})
describe('.paginate()', () => {
test('with a flat array', () => {
let query = new Query(arrayOf100Things)
.paginate()
.results
test('should paginate with default params', () => {
let query = new Query(TestData)
.paginate()
.results
expect(query.length).toBe(10)
})
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')
})
expect(query.length).toBe(9)
})
describe('.search()', () => {
test('partial string in flat array of strings', () => {
let query = new Query(['bar', 'foo', 'foobar', 'foofoobar'])
.search('foo')
.results
test('should paginate with custom page length', () => {
let query = new Query(TestData)
.paginate(1, 3)
.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)
.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()
})
expect(query.length).toBe(3)
expect(query[0].name).toBe('Haynes Meadows')
})
describe('.sort()', () => {
test('with flat array', () => {
let alphabetical = (a, b) => a > b
test('should paginate to second page with custom page length', () => {
let query = new Query(TestData)
.paginate(2, 3)
.results
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)
.sort(alphabetical)
.results
expect(query[0].name).toBe('Dudley Conner')
expect(query[query.length - 1].name).toBe('Wade Steele')
})
expect(query.length).toBe(3)
expect(query[0].name).toBe('Howard Buckley')
})
describe('.sortBy()', () => {
test('by boolean', () => {
let query = new Query(TestData)
.sortBy('isActive')
.results
test('should search by boolean isActive', () => {
let query = new Query(TestData)
.search('isActive', true)
.results
expect(query[0].name).toBe('Katelyn Steele')
})
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()
})
expect(query.length).toBe(4)
})
describe('.filter()', () => {
test('with a flat array', () => {
let isEven = a => !(a % 2)
test('should search by name', () => {
let query = new Query(TestData)
.search('name', 'steele')
.results
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 query = new Query(TestData)
.filter(isAgeOver33)
.results
expect(query[0].name).toBe('Howard Buckley')
})
expect(query.length).toBe(2)
})
describe('.filterBy()', () => {
test('by key', () => {
let isNumGT33 = num => num > 33
test('should sort by boolean isActive', () => {
let query = new Query(TestData)
.sort('isActive')
.results
let query = new Query(TestData)
.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()
})
expect(query[0].name).toBe('Katelyn Steele')
})
describe('chaining everything together', () => {
test('with a flat array', () => {
let query = new Query(['bar', 'foo', 'foobar', 'foofoobar'])
.search('foo')
.sort()
.paginate(1, 2)
.results
test('should sort by number netWorth', () => {
let query = new Query(TestData)
.sort('netWorth')
.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)
.search(true, 'isActive')
.sortBy('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')
})
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('should sort by string name', () => {
let query = new Query(TestData)
.sort('name')
.results
expect(query[0].name).toBe('Dudley Conner')
})
test('should filter', () => {
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",
"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",
"bugs": {
"url": "https://github.com/sharpshark28/json-query-chain/issues"