├── .gitattributes ├── .gitignore ├── ASTParsers ├── esprima.js ├── package.json ├── query.js ├── recast.js ├── transformFuncs.js └── transformQuery.js ├── Examples ├── Data │ ├── StarWars │ │ └── starWarsData.js │ ├── arrayOfComplexObjects.js │ ├── arrayOfObjects.js │ ├── arrayOfRecords.js │ └── arrayOfStrings.js ├── Queries │ └── simpleFilter.js ├── filterArray.js ├── filterRecords.js ├── flib.js ├── lib.js └── parSeqQueries.js └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js eol=lf 2 | *.txt eol=lf 3 | *.md eol=lf 4 | *.json eol=lf 5 | *.sh eol=lf 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | crlf.js 4 | -------------------------------------------------------------------------------- /ASTParsers/esprima.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | let esprima = require('esprima'); 4 | 5 | module.exports = { 6 | parse: esprima.parse, 7 | getProgram: expr => expr 8 | } 9 | -------------------------------------------------------------------------------- /ASTParsers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "esprima": "*", 4 | "recast": "*" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ASTParsers/query.js: -------------------------------------------------------------------------------- 1 | var query = `(person) => ( 2 | person.name !== '' && 3 | person.age > 18 && 4 | person.city === 'Rome' 5 | )`; 6 | 7 | module.exports = query; 8 | -------------------------------------------------------------------------------- /ASTParsers/recast.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | let recast = require('recast'); 4 | 5 | module.exports = { 6 | parse: recast.parse, 7 | getProgram: expr => expr.program, 8 | print: function (x) { 9 | return recast.print(x).code; 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /ASTParsers/transformFuncs.js: -------------------------------------------------------------------------------- 1 | function getExplicitKeyVal(expr, personVarName) { 2 | if (expr.type === 'BinaryExpression' && expr.operator === '===') { 3 | let lExpr = expr.left; 4 | if (lExpr.type === "MemberExpression") { 5 | let rExpr = expr.right; 6 | if (rExpr.type === 'Literal') { 7 | return [ lExpr.property.name, rExpr.value]; 8 | } 9 | } 10 | } 11 | return null; 12 | } 13 | 14 | function removeExplicitKeys(logExpr) { 15 | if (logExpr.type !== 'LogicalExpression' || logExpr.operator !== '&&') { 16 | let keyVal = getExplicitKeyVal(logExpr); 17 | return keyVal !== null 18 | ? [null, [keyVal]] 19 | : [Object.assign(logExpr), []]; 20 | } else { 21 | let [lRes, lKeyVals] = removeExplicitKeys(logExpr.left), 22 | [rRes, rKeyVals] = removeExplicitKeys(logExpr.right), 23 | allKeyVals = lKeyVals.concat(rKeyVals), 24 | allRes = lRes === null 25 | ? ( rRes === null 26 | ? null 27 | : rRes) 28 | : ( rRes === null 29 | ? lRes 30 | : Object.assign({}, logExpr)); 31 | return [allRes, allKeyVals]; 32 | } 33 | } 34 | 35 | module.exports = { 36 | getExplicitKeyVal: getExplicitKeyVal, 37 | removeExplicitKeys: removeExplicitKeys 38 | } 39 | -------------------------------------------------------------------------------- /ASTParsers/transformQuery.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | let parserName = process.argv[2] || 'recast', 4 | astParser = require('./'+parserName), 5 | util = require('util'); 6 | 7 | let query = require('./query'), 8 | tf = require('./transformFuncs'), 9 | ast = astParser.parse(query), 10 | astProgram = astParser.getProgram(ast), 11 | arrowFunction = astProgram.body[0].expression; 12 | 13 | let personVarName = arrowFunction.params[0].name, 14 | queryBody = arrowFunction.body; 15 | 16 | var newQueryBody, keyVals; 17 | [newQueryBody, keyVals] = tf.removeExplicitKeys(queryBody); 18 | 19 | arrowFunction.body = newQueryBody; 20 | 21 | console.log(keyVals); 22 | if (typeof astParser.print === 'function') 23 | console.log(astParser.print(ast)); 24 | -------------------------------------------------------------------------------- /Examples/Data/StarWars/starWarsData.js: -------------------------------------------------------------------------------- 1 | { 2 | humans: { 3 | 1000: { 4 | id: '1000', 5 | name: 'Luke Skywalker', 6 | friends: [ '1002', '1003', '2000', '2001' ], 7 | appearsIn: [ 4, 5, 6 ], 8 | homePlanet: 'Tatooine', 9 | }, 10 | 1001: { 11 | id: '1001', 12 | name: 'Darth Vader', 13 | friends: [ '1004' ], 14 | appearsIn: [ 4, 5, 6 ], 15 | homePlanet: 'Tatooine', 16 | }, 17 | 1002: { 18 | id: '1002', 19 | name: 'Han Solo', 20 | friends: [ '1000', '1003', '2001' ], 21 | appearsIn: [ 4, 5, 6 ], 22 | }, 23 | 1003: { 24 | id: '1003', 25 | name: 'Leia Organa', 26 | friends: [ '1000', '1002', '2000', '2001' ], 27 | appearsIn: [ 4, 5, 6 ], 28 | homePlanet: 'Alderaan', 29 | }, 30 | 1004: { 31 | id: '1004', 32 | name: 'Wilhuff Tarkin', 33 | friends: [ '1001' ], 34 | appearsIn: [ 4 ], 35 | }, 36 | }, 37 | droids: { 38 | 2000: { 39 | id: '2000', 40 | name: 'C-3PO', 41 | friends: [ '1000', '1002', '1003', '2001' ], 42 | appearsIn: [ 4, 5, 6 ], 43 | primaryFunction: 'Protocol', 44 | }, 45 | 2001: { 46 | id: '2001', 47 | name: 'R2-D2', 48 | friends: [ '1000', '1002', '1003' ], 49 | appearsIn: [ 4, 5, 6 ], 50 | primaryFunction: 'Astromech', 51 | }, 52 | }, 53 | characters: function() { 54 | return Object.assign({}, 55 | droids, 56 | humans 57 | ); 58 | }, 59 | } 60 | -------------------------------------------------------------------------------- /Examples/Data/arrayOfComplexObjects.js: -------------------------------------------------------------------------------- 1 | [ 2 | { name: 'Marcus Aurelius', birth: { day: '212-04-26', city: 'Rome' } }, 3 | { name: 'Victor Glushkov', birth: { day: '1923-08-24', city: 'Rostov on Don' } }, 4 | { name: 'Ibn Arabi', birth: { day: '1165-11-16', city: 'Murcia' } }, 5 | { name: 'Mao Zedong', birth: { day: '1893-12-26', city: 'Shaoshan' } }, 6 | { name: 'Rene Descartes', birth: { day: '1596-03-31', city: 'La Haye en Touraine' } } 7 | ] -------------------------------------------------------------------------------- /Examples/Data/arrayOfObjects.js: -------------------------------------------------------------------------------- 1 | [ 2 | { name: 'Marcus Aurelius', birth: new Date('212-04-26'), city: 'Rome' }, 3 | { name: 'Victor Glushkov', birth: new Date('1923-08-24'), city: 'Rostov on Don' }, 4 | { name: 'Ibn Arabi', birth: new Date('1165-11-16'), city: 'Murcia' }, 5 | { name: 'Mao Zedong', birth: new Date('1893-12-26'), city: 'Shaoshan' }, 6 | { name: 'Rene Descartes', birth: new Date('1596-03-31'), city: 'La Haye en Touraine' } 7 | ] -------------------------------------------------------------------------------- /Examples/Data/arrayOfRecords.js: -------------------------------------------------------------------------------- 1 | [ 2 | ['Marcus Aurelius','212-04-26','Rome'], 3 | ['Victor Glushkov','1923-08-24','Rostov on Don'], 4 | ['Ibn Arabi','1165-11-16','Murcia'], 5 | ['Mao Zedong','1893-12-26','Shaoshan'], 6 | ['Rene Descartes','1596-03-31','La Haye en Touraine'] 7 | ] -------------------------------------------------------------------------------- /Examples/Data/arrayOfStrings.js: -------------------------------------------------------------------------------- 1 | [ 2 | 'Marcus Aurelius Antoninus Augustus', 3 | 'Darth Vader', 4 | 'Victor Michailovich Glushkov', 5 | 'Gottfried Wilhelm von Leibniz', 6 | 'Mao Zedong', 7 | 'Vladimir Sergeevich Soloviov', 8 | 'Ibn Arabi', 9 | 'Lev Nikolayevich Tolstoy', 10 | 'Muammar Muhammad Abu Minyar al-Gaddafi', 11 | 'Rene Descartes', 12 | 'Fyodor Mikhailovich Dostoyevsky', 13 | 'Benedito de Espinosa' 14 | ] -------------------------------------------------------------------------------- /Examples/Queries/simpleFilter.js: -------------------------------------------------------------------------------- 1 | (person) => ( 2 | person.name !== '' && 3 | person.age > 18 && 4 | person.city === 'Rome' 5 | ) -------------------------------------------------------------------------------- /Examples/filterArray.js: -------------------------------------------------------------------------------- 1 | // Define Data Source 2 | 3 | var data = [ 4 | ['Marcus Aurelius','212-04-26','Rome'], 5 | ['Victor Glushkov','1923-08-24','Rostov on Don'], 6 | ['Ibn Arabi','1165-11-16','Murcia'], 7 | ['Mao Zedong','1893-12-26','Shaoshan'], 8 | ['Rene Descartes','1596-03-31','La Haye en Touraine'] 9 | ]; 10 | 11 | // Define Person prototype with calculating field 12 | 13 | var metadata = { 14 | name: 'string', 15 | birth: 'Date', 16 | city: 'string', 17 | age: function() { 18 | var difference = new Date() - this.birth; 19 | return Math.floor(difference / 31536000000); 20 | } 21 | }; 22 | 23 | // Build Prototype from Metadata 24 | 25 | function Person() {} 26 | 27 | var index = 0; 28 | for (var name in metadata) { 29 | buildGetter(Person.prototype, name, metadata[name], index++); 30 | } 31 | 32 | function buildGetter(proto, fieldName, fieldType, fieldIndex) { 33 | if (fieldType === 'Date') { 34 | Object.defineProperty(proto, fieldName, { 35 | get: function() { 36 | return new Date(this[fieldIndex]); 37 | } 38 | }); 39 | } else if (typeof(fieldType) === 'function') { 40 | Object.defineProperty(proto, fieldName, { get: fieldType }); 41 | } else { 42 | Object.defineProperty(proto, fieldName, { 43 | get: function() { 44 | return this[fieldIndex]; 45 | } 46 | }); 47 | } 48 | } 49 | 50 | // Define Query 51 | 52 | var query = (person) => ( 53 | person.name !== '' && 54 | person.age > 18 && 55 | person.city === 'Rome' 56 | ); 57 | 58 | // Assign prototype to array elements 59 | 60 | data.forEach((person) => { 61 | person.__proto__ = Person.prototype; 62 | }); 63 | 64 | // Filter Data using Query 65 | 66 | var res = data.filter(query); 67 | console.dir(res); 68 | 69 | module.exports = { 70 | data, 71 | query 72 | }; 73 | -------------------------------------------------------------------------------- /Examples/filterRecords.js: -------------------------------------------------------------------------------- 1 | // Define Data Source 2 | 3 | var data = [ 4 | { name: 'Marcus Aurelius', birth: new Date('212-04-26'), city: 'Rome' }, 5 | { name: 'Victor Glushkov', birth: new Date('1923-08-24'), city: 'Rostov on Don' }, 6 | { name: 'Ibn Arabi', birth: new Date('1165-11-16'), city: 'Murcia' }, 7 | { name: 'Mao Zedong', birth: new Date('1893-12-26'), city: 'Shaoshan' }, 8 | { name: 'Rene Descartes', birth: new Date('1596-03-31'), city: 'La Haye en Touraine' } 9 | ]; 10 | 11 | // Difine Person prototype with calculating field 12 | 13 | function Person() {} 14 | 15 | Person.prototype = { 16 | get age() { 17 | var difference = new Date() - this.birth; 18 | return Math.floor(difference / 31536000000); 19 | } 20 | }; 21 | 22 | // Define Query 23 | 24 | var query = (person) => ( 25 | person.name !== '' && 26 | person.age > 18 && 27 | person.city === 'Rome' 28 | ); 29 | 30 | // Assign prototype to array elements 31 | 32 | data.forEach((person) => { 33 | person.__proto__ = Person.prototype; 34 | }); 35 | 36 | // Filter Data using Query 37 | 38 | var res = data.filter(query); 39 | console.dir(res); 40 | -------------------------------------------------------------------------------- /Examples/flib.js: -------------------------------------------------------------------------------- 1 | function Monoid(append, empty) { 2 | if (empty !== undefined) { 3 | this.empty = empty; 4 | this.append = append; 5 | this.concat = arr => arr.reduce(append, empty); 6 | } else { 7 | let concat = append; 8 | this.concat = concat; 9 | this.empty = concat([]); 10 | this.append = (x, y) => concat([x, y]); 11 | } 12 | } 13 | 14 | function ParSeqMonoid(seqMonoid, parMonoid) { 15 | this.seqM = seqMonoid; 16 | this.parM = parMonoid; 17 | } 18 | 19 | function parSeq(data, parSeqMonoid) { 20 | let { seqM, parM } = parSeqMonoid, 21 | arrProccess = arr => arr.map( x => x instanceof Array 22 | ? parSeq(x, parSeqMonoid) 23 | : x); 24 | return data.length === 1 && data[0] instanceof Array 25 | ? parM.concat(arrProccess(data[0])) 26 | : seqM.concat(arrProccess(data)); 27 | } 28 | 29 | module.exports = { 30 | Monoid, 31 | ParSeqMonoid, 32 | parSeq 33 | } 34 | -------------------------------------------------------------------------------- /Examples/lib.js: -------------------------------------------------------------------------------- 1 | let { Monoid, ParSeqMonoid, parSeq } = require('./flib'); 2 | 3 | function Projection(f) { return f; } 4 | Projection.prototype = Function.prototype; 5 | 6 | function Filter(f) { return arr => arr.filter(f); } 7 | Filter.prototype = Function.prototype; 8 | 9 | function MyArr(arr) { 10 | return arr; 11 | } 12 | MyArr.prototype = Array.prototype; 13 | 14 | function myApply(f, arr) { 15 | return arr instanceof MyArr 16 | ? arr.map(child => myApply(f, child)) 17 | : f(arr); 18 | } 19 | 20 | let queryParSeqMonoid = new ParSeqMonoid( 21 | new Monoid((x, y) => arr => myApply(y, myApply(x, arr)), x => x), 22 | new Monoid(funcArr => myarr => funcArr.map(f => myApply(f, myarr))) 23 | ), 24 | queryParSeq = x => parSeq(x, queryParSeqMonoid); 25 | 26 | module.exports = { 27 | Projection, 28 | Filter, 29 | queryParSeq 30 | }; 31 | -------------------------------------------------------------------------------- /Examples/parSeqQueries.js: -------------------------------------------------------------------------------- 1 | let { Projection, Filter, queryParSeq } = require('./lib'), 2 | { Monoid, ParSeqMonoid, parSeq } = require('./flib'), 3 | filt = require('./filterArray'); 4 | 5 | let inRomeFilter = Filter(person => person.city === 'Rome'), 6 | inCreteFilter = Filter(person => person.city === 'Crete'), 7 | nameProjection = Projection((metadata, proto) => ['name']), 8 | query = [ [[ inRomeFilter, inCreteFilter ]], nameProjection ]; 9 | 10 | let strParSeqMonoid = new ParSeqMonoid( 11 | new Monoid((x, y) => x + y, ''), 12 | new Monoid((x, y) => x !== '' 13 | ? y !== '' 14 | ? '{' + x + '|' + y + '}' 15 | : x 16 | : y !== '' 17 | ? y 18 | : '', '') 19 | ), 20 | strParSeq = x => parSeq(x, strParSeqMonoid); 21 | 22 | console.log(strParSeq([ 23 | [[ ['123', '321'], [['при', 'пере']] ]], 24 | ['456', '789'], 25 | 'ABC' 26 | ])); 27 | 28 | let simpleQuery = [ 29 | x => ({ x, y: 4 }), 30 | [[ x => x.x, [x => x.y, x => x + 1] ]] 31 | ]; 32 | 33 | console.log(queryParSeq(simpleQuery)([1,2,3])); 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSQL / JavaScript Query Language 2 | --------------------------------------------------------------------------------