Skip to content
Snippets Groups Projects
Verified Commit e432921a authored by Isabella Skořepová's avatar Isabella Skořepová
Browse files

Implement simple article queriing utility

Example usage:

node query.js --sql "select metadata.tags as tags, metadata.title as title where count(metadata.tags) == 1 and contains(metadata.tags, 'Článek');"

prints list of articles which have only tag "Článek" - usefull for
listing not yet tagged articles
parent a7b8f218
No related branches found
No related tags found
No related merge requests found
......@@ -23,6 +23,7 @@
"nodegit": "^0.11.7",
"nunjucks": "^2.3.0",
"nunjucks-date-filter": "^0.1.1",
"sqlite-parser": "^0.14.3",
"syntax-error": "^1.1.5",
"toml": "^2.3.0",
"toml-js": "0.0.8",
......
query.js 0 → 100644
var sqliteParser = require('sqlite-parser');
var cli = require('cli');
var options = cli.parse({
contentdir: [null, 'Allows to specify arbitrary content directory.', 'string', 'content'],
sql: ['q', 'SQL querry', 'string']
});
var ast = sqliteParser(options.sql).statement[0];
console.log(JSON.stringify(ast,null,' '));
var getProp = function(prop, article) {
var ret = article;
prop.split('.').forEach(function(p) {
if(ret === undefined) return;
ret = ret[p];
});
return ret;
}
var functions = {
'contains': function(context, args) {
if(args.length !== 2) throw new Error('Wrong number of arguments for function contains');
var prop = evalExpr(context, args[0]);
if(!Array.isArray(prop)) return false;
var val = evalExpr(context, args[1]);
var ret = false;
prop.forEach(function(el) {
if(el == val) ret = true;
})
return ret;
},
'count': function(context, args) {
if(args.length !== 1) throw new Error('Wrong number of arguments for function count');
var prop = evalExpr(context, args[0]);
if(prop === undefined) return 0;
if(!Array.isArray(prop)) return 1;
return prop.length;
}
}
var binaryOperators = {
'and': function(context, left, right) {
return evalExpr(context, left) && evalExpr(context, right);
},
'or': function(context, left, right) {
return evalExpr(context, left) || evalExpr(context, right);
},
'==': function(context, left, right) {
return evalExpr(context, left) == evalExpr(context, right);
},
'>': function(context, left, right) {
return evalExpr(context, left) > evalExpr(context, right);
},
'<': function(context, left, right) {
return evalExpr(context, left) < evalExpr(context, right);
},
'>=': function(context, left, right) {
return evalExpr(context, left) >= evalExpr(context, right);
},
'<=': function(context, left, right) {
return evalExpr(context, left) <= evalExpr(context, right);
},
'!=': function(context, left, right) {
return evalExpr(context, left) != evalExpr(context, right);
},
}
var literals = {
'decimal': function(context, value) {
return Number(value);
},
'string': function(context, value) {
return value;
}
}
var evalExpr = function(context, expr) {
if(expr.type == 'identifier') {
return getProp(expr.name, context);
} else if(expr.type == 'expression' && expr.format == 'binary') {
if(typeof binaryOperators[expr.operation] === 'function') {
return binaryOperators[expr.operation](context, expr.left, expr.right);
} else {
throw new Error('Unsupported binary operator '+expr.operation);
}
} else if(expr.type == 'function') {
if(typeof functions[expr.name] === 'function') {
return functions[expr.name](context, expr.args);
} else {
throw new Error('Unsupported function '+expr.name);
}
} else if(expr.type == 'literal') {
if(typeof literals[expr.variant] === 'function') {
return literals[expr.variant](context, expr.value);
} else {
throw new Error('Unsupported literal variant '+expr.variant);
}
} else {
throw new Error('Unsupported expression '+JSON.stringify(expr));
}
throw new Error('Code should not get here. This is really bad.');
}
var jobs = require('./sitegin/jobs');
jobs.registerMultiple(
{},
['parseHugo', './parseHugo'],
['readFiles', './readFiles']
)
.then(function() {
return jobs.run('readFiles',{config:{
sourceDir: options.contentdir,
articlesLocation: 'articles',
redirectsLocation: 'redirects'
}})
})
.then(function(obj) {
return jobs.run('parseHugo',obj)
})
.then(function(obj){
return obj.pages
})
.then(function(pages) {
return pages.filter(function(page) {
return evalExpr(page, ast.where[0]);
})
})
.then(function(pages) {
var results = ast.result;
if(results[0].variant == 'star') {
pages.forEach(function(page) {
console.log(JSON.stringify(page));
})
return;
}
pages.forEach(function(page) {
var n = {};
results.forEach(function(result) {
if(result.alias) n[result.alias] = getProp(result.name, page);
else n[result.name] = getProp(page, result.name);
})
console.log(n);
})
})
.then(function() {
process.exit(0);
})
.catch(function(e) {
console.log(e.stack);
process.exit(1);
})
'use strict';
/*
* This file specifies whole configuration of sitegin.
* In future it might actually read configuration files but for now - if you
* want to configure sitegin you have to modify this file.
* (or write config reader which would replace this file - I'd appreciate
* merge request for this feature ;) )
* It reads config file from content/config.toml it also parses command-line
* arguments. CLI args take precedence over config options
*/
var Git = require('nodegit');
var cli = require('cli');
var fs = require('fs');
var path = require('path');
var toml = require('toml');
var options = cli.parse({
noserver: ['n', 'Dont run server'],
port: ['p', 'Port on which server should run', 'number', 1337],
......@@ -30,20 +32,25 @@ module.exports = function() {
options.uiport = options.port+1;
}
return new Promise(function(resolve, reject){
function doResolve() {
resolve({config: {
options: options,
builddir: builddir,
sourceDir: options.contentdir,
staticDir: options.staticdir,
themeDir: options.themedir,
articlesLocation: 'articles',
redirectsLocation: 'redirects',
linksPerPage: 6
}});
}
doResolve();
return new Promise(function(resolve, reject) {
var config = {
options: options,
builddir: builddir,
sourceDir: options.contentdir,
staticDir: options.staticdir,
themeDir: options.themedir,
articlesLocation: 'articles',
redirectsLocation: 'redirects',
linksPerPage: 6
};
var configFile = path.join(config.sourceDir, 'config.toml');
fs.readFile(configFile,'utf-8',function(err,data) {
if(err) return reject('Error reading '+configFile);
data = toml.parse(data);
console.log(data);
for(let attr in config) { data[attr] = config[attr]; }
resolve({config: data});
})
})
}
module.exports.watch = !options.noserver;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment