Skip to content

Instantly share code, notes, and snippets.

@jmakeig
Last active February 17, 2017 23:20
Show Gist options
  • Select an option

  • Save jmakeig/bf8dfcff1111cdb7682de578be12f1f7 to your computer and use it in GitHub Desktop.

Select an option

Save jmakeig/bf8dfcff1111cdb7682de578be12f1f7 to your computer and use it in GitHub Desktop.
Experiments with typing tabular output from the MarkLogic Optic API
'use strict';
function RowSequence(rows) {
//if(!(this instanceof RowSequence)) { return new RowSequence(rows); }
this._rows = rows;
}
RowSequence.prototype = new Sequence;
RowSequence.prototype[Symbol.iterator] = function*() { yield* this._rows; } // This won’t stream
const op = require('/MarkLogic/optic');
const plan =
op.fromLiterals([
{group:1, val:2},
{group:1, val:4},
{group:2, val:3},
{group:2, val:5},
{group:2, val:7}
])
.orderBy('group')
.groupBy('group', op.avg('valAvg', 'val'));
new RowSequence(
plan.result('object')
);
'use strict';
/**
* A wrapper class to expose a proper iterator on anything that’s “array-like”.
* Criteria for being array-like:
* 1) has a `length` property that’s a positive integer
* 2) has items that are accessible with integer indexes
* @class
*/
function ArrayLikeIterator (obj) {
this._o = obj;
this._i = 0;
}
ArrayLikeIterator.prototype[Symbol.iterator] = function () {
return this;
}
ArrayLikeIterator.prototype.next = function () {
if (this._o === void 0 || this._i >= this._o.length) {
this._o = void 0;
return { value: void 0, done: true }
}
return { value: this._o[this._i++], done: false }
}
/**
* Base type. Just a marker interface so both `RowArray` and `RowObject` instances
* share a common type.
* @class
*/
function Row(){}
/**
* Row results expressible as array-like.
* FIXME: Does’t currently handle headers.
* @class
* @extends Row
*/
function RowArray(values) {
Row.apply(this);
// Make it array-like without actually extending `Array`
Object.defineProperty(
this,
'length',
{
value: values.length,
configurable: false,
enumerable: false,
writable: false,
}
);
// Yes, this is inefficient.
for(let i = 0; i < values.length; i++) {
// Array-like
this[i] = values[i];
}
Object.freeze(this);
}
RowArray.prototype = new Row;
/**
* @property {function} - An iterator to make `RowArray` iterable over its column values
*/
RowArray.prototype[Symbol.iterator] = function() { return new ArrayLikeIterator(this); }
/**
* Sibling of `RowArray` that has columns exposed as enumerable properties on the instance.
* @class
* @extends Row
*/
function RowObject(cols) {
Row.apply(this);
const config = { enumerable: true, writable: false, configurable: false };
for(const col in cols) {
Object.defineProperty(this, col, Object.assign(config, {value: cols[col]}));
}
Object.freeze(this);
}
RowObject.prototype = new Row;
RowObject.prototype.toObject = function() {
return Object.assign({}, this);
}
function doOptic(plan, output) {
switch(output) {
case 'array':
return Sequence.from(plan.result('array'), row => new RowArray(row));
case 'object':
return Sequence.from(plan.result('object'), row => new RowObject(row));
default:
throw new TypeError(`${String(output)} is not a supported type`);
}
}
const op = require('/MarkLogic/optic');
const plan =
op.fromLiterals([
{group:1, val:2},
{group:1, val:4},
{group:2, val:3},
{group:2, val:5},
{group:2, val:7}
])
.orderBy('group')
.groupBy('group', op.avg('valAvg', 'val'));
Sequence.from(
doOptic(plan, 'array'),
row => { return { typed: row instanceof RowArray && row instanceof Row, values: Array.from(row) } }
);
// {"typed":true, "values":["group", "valAvg"]}
// {"typed":true, "values":[1, 3]}
// {"typed":true, "values":[2, 5]}
Sequence.from(
doOptic(plan, 'object'),
row => { return { typed: row instanceof RowObject && row instanceof Row, values: row.toObject() } }
);
// {"typed":true, "values":{"group":1, "valAvg":3}}
// {"typed":true, "values":{"group":2, "valAvg":5}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment