Compare commits

..

No commits in common. "v1.0.0" and "main" have entirely different histories.
v1.0.0 ... main

10 changed files with 268 additions and 77 deletions

11
.eslintrc.js Normal file
View file

@ -0,0 +1,11 @@
module.exports = {
"extends": "standard",
"plugins": [
"standard",
"promise",
"jest"
],
"env": {
"jest/globals": true
}
};

1
.gitignore vendored
View file

@ -1 +1,2 @@
node_modules node_modules
./coverage

View file

@ -1,3 +1,19 @@
env:
global:
- CC_TEST_REPORTER_ID=e1449b0f2e8eba8f08c56a292243145925aeb4f521e5682667ac6d704829ba4c
- GIT_COMMITTED_AT=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then git log -1 --pretty=format:%ct; else git log -1 --skip 1 --pretty=format:%ct; fi)
language: node_js language: node_js
node_js: node_js:
- "7" - "7"
before_script:
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
- chmod +x ./cc-test-reporter
script:
- npm run test
# Preferably you will run test-reporter on branch update events. But
# if you setup travis to build PR updates only, you don't need to run
# the line below
- if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT; fi
# In the case where travis is setup to build PR updates only,
# uncomment the line below
# - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT

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>

85
README.md Normal file
View file

@ -0,0 +1,85 @@
# json-query-chain
[![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 POJOs to return precise results.
## Usage
```javascript
import Query from 'json-query-chain';
let myQ = new Query(someJsonData)
.search('isActiveUser', true)
.results;
```
### Chainable Methods
#### Search
Currently supports booleans and strings. (See [#1](https://github.com/sharpshark28/json-query-chain/issues/1) for Integer Support)
##### By Boolean
```javascript
.search('isActiveUser', true)
```
##### By String
```javascript
.search('name', 'steele')
```
#### Filter
Simpler version of search using a custom function in the chain.
```javascript
.filter(a => a.age >= 21)
```
##### By Key
```javascript
.filterBy('age', x => x >= 21)
```
#### Sort
##### By Boolean
```javascript
.sort('isActiveUser', true)
```
##### By String
```javascript
.sort('name')
```
##### By Number
```javascript
.sort('netWorth')
```
#### Pagination
Page 1 with 5 results per page.
```javascript
.paginate(1, 5)
```
Page 2 wtih default of 10 results per page.
```javascript
.paginate(2)
```
## Tests
`npm test` runs tests through Jest

1
coverage.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="114" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="114" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h62v20H0z"/><path fill="#4C1" d="M62 0h52v20H62z"/><path fill="url(#b)" d="M0 0h114v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,DejaVu Sans,Geneva,sans-serif" font-size="11"><text x="31" y="15" fill="#010101" fill-opacity=".3">coverage</text><text x="31" y="14">coverage</text><text x="87" y="15" fill="#010101" fill-opacity=".3">97.22%</text><text x="87" y="14">97.22%</text></g></svg>

After

Width:  |  Height:  |  Size: 735 B

View file

@ -1,47 +1,55 @@
module.exports = class Query { module.exports = class Query {
constructor (data) { constructor (data) {
this.data = data.map(item => { this.data = data.map(item => {
item.sortScore = 0; item.sortScore = 0
return item; return item
}); })
}; }
get results () { get results () {
return this.data; return this.data
}; }
filter (func) {
this.data = this.data.filter(func)
return this
}
filterBy (key, func) {
this.data = this.data.filter(item => func(item[key]))
return this
}
search (key, term, score = 0) { search (key, term, score = 0) {
switch (typeof term) { switch (typeof term) {
case 'boolean': case 'boolean':
this.data = this.data.filter(item => item[key] === term); this.data = this.data.filter(item => item[key] === term)
break; break
case 'string': case 'string':
if (term.length >= 3) { this.data = this.data.filter(item => {
this.data = this.data.filter(item => { let regFind = new RegExp(term, 'gi')
let regFind = new RegExp(term, 'gi'); let termMatches = (item[key].match(regFind) || []).length
let termMatches = (item[key].match(regFind) || []).length; item.sortScore += termMatches
item.sortScore += termMatches; return termMatches
return termMatches; })
}); break
}
break;
} }
return this; return this
}; }
sort (key = 'sortScore') { sort (key = 'sortScore') {
this.data = this.data.sort((a, b) => { 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
}); })
return this; return this
}; }
paginate (page = 1, perPage = 10) { paginate (page = 1, perPage = 10) {
let min = page * perPage - perPage; let min = page * perPage - perPage
let max = min + perPage; let max = min + perPage
this.data = this.data.slice(min, max); this.data = this.data.slice(min, max)
return this; return this
}; }
}; }

View file

@ -1,79 +1,109 @@
const Query = require('./index'); const Query = require('./index')
const TestData = require('./testdata.json'); const TestData = require('./testdata.json')
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
expect(query).toMatchObject(TestData); expect(query).toMatchObject(TestData)
}); })
test('should paginate with default params', () => { test('should paginate with default params', () => {
let query = new Query(TestData) let query = new Query(TestData)
.paginate() .paginate()
.results; .results
expect(query.length).toBe(9); expect(query.length).toBe(9)
}); })
test('should paginate with custom page length', () => { 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
expect(query.length).toBe(3); expect(query.length).toBe(3)
expect(query[0].name).toBe('Haynes Meadows') expect(query[0].name).toBe('Haynes Meadows')
}); })
test('should paginate to second page with custom page length', () => { 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
expect(query.length).toBe(3); expect(query.length).toBe(3)
expect(query[0].name).toBe('Howard Buckley') expect(query[0].name).toBe('Howard Buckley')
}); })
test('should search by boolean isActive', () => { test('should search by boolean isActive', () => {
let query = new Query(TestData) let query = new Query(TestData)
.search('isActive', true) .search('isActive', true)
.results; .results
expect(query.length).toBe(4); expect(query.length).toBe(4)
}); })
test('should search by age', () => { test('should search by name', () => {
let query = new Query(TestData) let query = new Query(TestData)
.search('name', 'steele') .search('name', 'steele')
.results; .results
expect(query.length).toBe(2); expect(query.length).toBe(2)
}); })
test('should sort by boolean isActive', () => { test('should sort by boolean isActive', () => {
let query = new Query(TestData) let query = new Query(TestData)
.sort('isActive') .sort('isActive')
.results; .results
expect(query[0].name).toBe('Katelyn Steele'); expect(query[0].name).toBe('Katelyn Steele')
}); })
test('should sort by number netWorth', () => {
let query = new Query(TestData)
.sort('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('should sort by string name', () => { test('should sort by string name', () => {
let query = new Query(TestData) let query = new Query(TestData)
.sort('name') .sort('name')
.results; .results
expect(query[0].name).toBe('Dudley Conner'); 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', () => { test('should chain everything together', () => {
let query = new Query(TestData) let query = new Query(TestData)
.search('isActive', true) .search('isActive', true)
.sort('name') .sort('name')
.paginate(1, 2) .paginate(1, 2)
.results; .results
expect(query.length).toBe(2); expect(query.length).toBe(2)
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

@ -1,22 +1,32 @@
{ {
"name": "json-query-chain", "name": "json-query-chain",
"version": "1.0.0", "version": "1.1.3",
"description": "Chain queries onto POJOs to return precise results.", "description": "Chain queries onto POJOs to return precise results.",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "jest" "test": "jest --coverage && npm run badge",
"badge": "lcov-badge coverage/lcov.info -o coverage.svg",
"lint": "eslint ./*.js"
}, },
"repository": { "repository": {
"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"
}, },
"homepage": "https://github.com/sharpshark28/json-query-chain#readme", "homepage": "https://github.com/sharpshark28/json-query-chain#readme",
"devDependencies": { "devDependencies": {
"jest": "^20.0.4" "eslint": "^4.18.1",
"eslint-config-standard": "^11.0.0",
"eslint-plugin-import": "^2.9.0",
"eslint-plugin-jest": "^21.12.2",
"eslint-plugin-node": "^6.0.0",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-standard": "^3.0.1",
"jest": "^20.0.4",
"lcov-badge": "^1.0.4"
} }
} }

View file

@ -3,6 +3,7 @@
"_id": "5928038b69b33613f3577487", "_id": "5928038b69b33613f3577487",
"isActive": true, "isActive": true,
"age": 20, "age": 20,
"netWorth": 54200.49,
"name": "Haynes Meadows", "name": "Haynes Meadows",
"tags": [ "tags": [
"et", "et",
@ -18,6 +19,7 @@
"_id": "5928038ba2400e7e09550991", "_id": "5928038ba2400e7e09550991",
"isActive": false, "isActive": false,
"age": 22, "age": 22,
"netWorth": 11900,
"name": "Katelyn Steele", "name": "Katelyn Steele",
"tags": [ "tags": [
"cupidatat", "cupidatat",
@ -33,6 +35,7 @@
"_id": "5928038bff4df5c29016be22", "_id": "5928038bff4df5c29016be22",
"isActive": false, "isActive": false,
"age": 29, "age": 29,
"netWorth": 0,
"name": "Natalia Petty", "name": "Natalia Petty",
"tags": [ "tags": [
"nulla", "nulla",
@ -48,6 +51,7 @@
"_id": "5928038b496f64e646cb8d69", "_id": "5928038b496f64e646cb8d69",
"isActive": false, "isActive": false,
"age": 34, "age": 34,
"netWorth": -5000.2,
"name": "Howard Buckley", "name": "Howard Buckley",
"tags": [ "tags": [
"qui", "qui",
@ -63,6 +67,7 @@
"_id": "5928038b4d214186ba1e7fa2", "_id": "5928038b4d214186ba1e7fa2",
"isActive": false, "isActive": false,
"age": 22, "age": 22,
"netWorth": 401512.99,
"name": "Jenkins Mosley", "name": "Jenkins Mosley",
"tags": [ "tags": [
"adipisicing", "adipisicing",