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':
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
} }

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,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')
}) })
})

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"