-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
2,127 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import has from 'underscore/modules/_has.js'; | ||
import isFunction from 'underscore/modules/isFunction.js'; | ||
|
||
import { array } from 'helpers/array'; | ||
import { log, warn } from 'helpers/log'; | ||
|
||
import { model } from './data'; | ||
import { single } from './model'; | ||
|
||
export async function cascade(data, fields, expeditor) { | ||
if (data.length === 0 || fields.length === 0) { | ||
return; | ||
} | ||
|
||
if (has(expeditor, 'children')) { | ||
var previous = expeditor.children; | ||
} | ||
|
||
var children = expeditor.children = []; | ||
|
||
if (previous) { | ||
children._previous = previous; | ||
} | ||
|
||
collect(data, fields, expeditor); | ||
|
||
var promises = children.map(expeditor => | ||
expeditor.sequent() | ||
); | ||
|
||
var res = await Promise.all(promises); | ||
|
||
promises = children.map((expeditor, i) => { | ||
if (expeditor.fields) { | ||
return cascade(res[i], expeditor.fields, expeditor); | ||
} | ||
}); | ||
|
||
return Promise.all(promises); | ||
} | ||
|
||
function collect(data, fields, expeditor) { | ||
var m = model(expeditor.model); | ||
|
||
fields.forEach(field => { | ||
var sequence = m._parse(field); | ||
var step = sequence.find(step => step.field); | ||
var index = sequence.indexOf(step); | ||
var info = m._info(step.field); | ||
|
||
var tailField = sequence | ||
.slice(index + 1) | ||
.map(step => step.chunk) | ||
.join('.'); | ||
|
||
if (info.model) { | ||
propagate(data, step, info, tailField, expeditor); | ||
} | ||
else if (isFunction(m[step.field])) { | ||
var calculated = | ||
m[step.field + 'Field'] || | ||
m[step.field].field; | ||
|
||
if (calculated) { | ||
fields = array(calculated); | ||
fields = single(fields); | ||
log('expand "' + step.field + '" to', { fields }); | ||
collect(data, fields, expeditor); | ||
} | ||
} | ||
else if (m.fieldsMap[step.field] >= 0) { | ||
// field belongs to model | ||
} | ||
else { | ||
warn('model "' + m.aka + '"', | ||
'has no property "' + step.field + '"', | ||
'in field "' + field + '"'); | ||
} | ||
}); | ||
} | ||
|
||
function propagate(data, step, info, tailField, expeditor) { | ||
var children = expeditor.children; | ||
|
||
var k = [ | ||
info.model, | ||
step.field, | ||
step.filter && step.filter.list.map(filter => filter.expression), | ||
]; | ||
|
||
var child = children[k]; | ||
|
||
if (!child) { | ||
children[k] = child = spawn(data, step, info, expeditor); | ||
children.push(child); | ||
} | ||
|
||
if (tailField) { | ||
child.fields.push(tailField); | ||
} | ||
} | ||
|
||
function spawn(data, step, info, expeditor) { | ||
var fields = []; | ||
var params = {}; | ||
|
||
params[info.index] = data.map(row => row[info.field]); | ||
|
||
if (step.filter) { | ||
step.filter.list.forEach(filter => { | ||
if (filter.operator === '=' && filter.value.indexOf('..') === -1) { | ||
params[filter.field] = filter.value.split(','); | ||
} | ||
else { | ||
fields.push(filter.field); | ||
} | ||
}); | ||
} | ||
|
||
return expeditor.spawn({ | ||
field: step.field, | ||
nullable: step.nullable, | ||
|
||
inversed: [ info.inverse || expeditor.model ].concat(expeditor.inversed), // copy | ||
index: info.index, | ||
model: info.model, | ||
|
||
fields, | ||
params, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
import { nativeIsArray, nativeKeys } from 'underscore/modules/_setup.js'; | ||
|
||
import { applyOwnIf } from 'helpers/apply'; | ||
import { isObject } from 'helpers/is'; | ||
import { log, raise } from 'helpers/log'; | ||
import { measure } from 'helpers/measure'; | ||
|
||
import { collection, model } from './data'; | ||
import { Index } from './index'; | ||
|
||
export function register(name, records) { | ||
if (!(c = collection(name, !!records))) { | ||
var m = model(name); | ||
var c = new Collection(m.aka); | ||
|
||
collection(m.name, c); | ||
collection(m.aka, c); | ||
} | ||
|
||
if (records) { | ||
c.splice(records); | ||
} | ||
|
||
return c; | ||
} | ||
|
||
export class Collection { | ||
constructor(name) { | ||
this.indexes = {}; | ||
this.index(''); | ||
|
||
if (name) { | ||
this.model = name; | ||
} | ||
|
||
if (this.initialize) { | ||
this.initialize(name); | ||
} | ||
} | ||
|
||
index(...keys) { | ||
var k = keys.length < 2 ? keys[0] || '' : keys.join('-'); | ||
var i = this.indexes[k]; | ||
var m = model(this.model); | ||
|
||
if (!i) { | ||
keys = k.split('-'); | ||
i = this.indexes[k] = new Index(keys); | ||
|
||
if (k) { | ||
measure('create index in "' + this.model + '" for keys ' + keys, () => { | ||
this.find().forEach(i.register, i); | ||
}); | ||
} | ||
|
||
if (m) { | ||
var onetime = keys.some(k => { | ||
var step = m._parse(k).find(step => step.field); | ||
var info = m._info(step.field); | ||
|
||
// one-to-many relation | ||
return info.model && info.index !== 'id'; | ||
}); | ||
|
||
if (onetime) { | ||
log(this, 'one-time', { index: i }); | ||
delete this.indexes[k]; | ||
} | ||
} | ||
} | ||
|
||
return i; | ||
} | ||
|
||
splice() { | ||
measure('splice "' + this.model + '"', this._splice, this, arguments); | ||
} | ||
|
||
_splice(records, doRemove) { | ||
var m = model(this.model); | ||
var i = this.index(m && m.origin || 'id'); | ||
|
||
var record = records[0]; | ||
var isTuple = nativeIsArray(record); | ||
var isRecord = m && record instanceof m.constructor; | ||
|
||
for (var j = 0; j < records.length; j++) { | ||
record = records[j]; | ||
|
||
if (m) { | ||
if (isTuple || !isRecord) { | ||
record = records[j] = m.create(record); | ||
} | ||
} | ||
|
||
var values = i.records(record); | ||
var exist = values && values[0]; | ||
|
||
if (exist && !isTuple && !isRecord) { | ||
applyOwnIf(record, exist); | ||
} | ||
|
||
if (doRemove) { | ||
if (exist) { | ||
records[j] = exist; | ||
} | ||
else { | ||
records.splice(j--, 1); | ||
} | ||
} | ||
|
||
for (var k in this.indexes) { | ||
if (exist) { | ||
this.indexes[k].unregister(exist); | ||
} | ||
|
||
if (!doRemove) { | ||
this.indexes[k].register(record); | ||
} | ||
} | ||
} | ||
} | ||
|
||
find(params = {}, buffer) { | ||
if (!isObject(params)) { | ||
params = { id: params }; | ||
} | ||
|
||
var keys = nativeKeys(params); | ||
|
||
return this | ||
.index(...keys) | ||
.select(params, buffer); | ||
} | ||
|
||
findOne(params) { | ||
return this.find(params)[0]; | ||
} | ||
|
||
findOrFail(params) { | ||
var record = this.find(params)[0]; | ||
|
||
if (record) { | ||
return record; | ||
} | ||
|
||
raise('not found', this.model, 'using', JSON.stringify(params)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import has from 'underscore/modules/_has.js'; | ||
import { nativeCreate } from 'underscore/modules/_setup.js'; | ||
import noop from 'underscore/modules/noop.js'; | ||
|
||
import { applyOwn } from 'helpers/apply'; | ||
|
||
var collections = {}; | ||
const models = {}; | ||
|
||
export const data = { | ||
collections, | ||
extras: {}, | ||
indexes, | ||
models, | ||
similars: {}, | ||
views: {}, | ||
}; | ||
|
||
export const fetchRefId = { fetchRefId: true }; | ||
|
||
export const opt = { | ||
akaRe: '', | ||
classify: false, | ||
extra: false, | ||
keySorter: false, // (a, b) => a <=> b | ||
request: noop, | ||
}; | ||
|
||
export function init(config) { | ||
return applyOwn(opt, config); | ||
} | ||
|
||
export function fork() { | ||
collections = data.collections = nativeCreate(collections); | ||
} | ||
|
||
export function free() { | ||
collections = data.collections = Object.getPrototypeOf(collections); | ||
} | ||
|
||
/** | ||
* 'set' = true - find in fork | ||
* 'set' = falsy - find in collections | ||
* 'set' = smth - save in current storage | ||
*/ | ||
export function collection(name, set) { | ||
if (set === true) { | ||
if (!has(collections, name)) { | ||
return; | ||
} | ||
} | ||
else if (set) { | ||
collections[name] = set; | ||
} | ||
|
||
return collections[name]; | ||
} | ||
|
||
export function model(name, set) { | ||
if (set) { | ||
models[name] = set; | ||
} | ||
|
||
return models[name]; | ||
} | ||
|
||
export function find(name, params, buffer) { | ||
return collection(name).find(params, buffer); | ||
} | ||
|
||
export function findOne(name, params) { | ||
return collection(name).findOne(params); | ||
} | ||
|
||
export function findOrFail(name, params) { | ||
return collection(name).findOrFail(params); | ||
} | ||
|
||
export function index(name, ...keys) { | ||
return collection(name).index(...keys); | ||
} | ||
|
||
function indexes() { | ||
var hash = {}; | ||
|
||
for (var name in collections) { | ||
hash[name] = collections[name].indexes; | ||
} | ||
|
||
return hash; | ||
} |
Oops, something went wrong.