schema.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. /**
  2. * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { Database, Table, Column, ColumnHelpers } from 'mol-data/db'
  7. import { Tensor } from 'mol-math/linear-algebra'
  8. import * as Data from './data-model'
  9. export function toDatabase<Schema extends Database.Schema, Frame extends Database<Schema> = Database<Schema>>(schema: Schema, frame: Data.Frame): Frame {
  10. return createDatabase(schema, frame) as Frame;
  11. }
  12. export function toTable<Schema extends Table.Schema, R extends Table<Schema> = Table<Schema>>(schema: Schema, category: Data.Category): R {
  13. return new CategoryTable(category, schema, true) as any;
  14. }
  15. type ColumnCtor = (field: Data.Field, category: Data.Category, key: string) => Column<any>
  16. function getColumnCtor(t: Column.Schema): ColumnCtor {
  17. switch (t.valueType) {
  18. case 'str': return (f, c, k) => createColumn(t, f, f.str, f.toStringArray);
  19. case 'int': return (f, c, k) => createColumn(t, f, f.int, f.toIntArray);
  20. case 'float': return (f, c, k) => createColumn(t, f, f.float, f.toFloatArray);
  21. case 'tensor': throw new Error(`Use createTensorColumn instead.`);
  22. }
  23. }
  24. function createColumn<T>(schema: Column.Schema, field: Data.Field, value: (row: number) => T, toArray: Column<T>['toArray']): Column<T> {
  25. return {
  26. schema,
  27. '@array': field['@array'],
  28. isDefined: field.isDefined,
  29. rowCount: field.rowCount,
  30. value,
  31. valueKind: field.valueKind,
  32. areValuesEqual: field.areValuesEqual,
  33. toArray
  34. };
  35. }
  36. function createTensorColumn(schema: Column.Schema.Tensor, category: Data.Category, key: string): Column<Tensor> {
  37. const space = schema.space;
  38. const first = category.getField(`${key}[1]`) || Column.Undefined(category.rowCount, schema);
  39. const value = (row: number) => Data.getTensor(category, key, space, row);
  40. const toArray: Column<Tensor>['toArray'] = params => ColumnHelpers.createAndFillArray(category.rowCount, value, params)
  41. return {
  42. schema,
  43. '@array': void 0,
  44. isDefined: first.isDefined,
  45. rowCount: category.rowCount,
  46. value,
  47. valueKind: first.valueKind,
  48. areValuesEqual: (rowA, rowB) => Tensor.areEqualExact(value(rowA), value(rowB)),
  49. toArray
  50. };
  51. }
  52. class CategoryTable implements Table<any> { // tslint:disable-line:class-name
  53. _rowCount: number;
  54. _columns: ReadonlyArray<string>;
  55. _schema: any;
  56. [k: string]: any;
  57. constructor(category: Data.Category, schema: Table.Schema, public _isDefined: boolean) {
  58. const fieldKeys = Object.keys(schema);
  59. this._rowCount = category.rowCount;
  60. this._columns = fieldKeys;
  61. this._schema = schema;
  62. const cache = Object.create(null);
  63. for (const k of fieldKeys) {
  64. Object.defineProperty(this, k, {
  65. get: function() {
  66. if (cache[k]) return cache[k];
  67. const fType = schema[k];
  68. if (fType.valueType === 'tensor') {
  69. cache[k] = createTensorColumn(fType, category, k);
  70. } else {
  71. const ctor = getColumnCtor(fType);
  72. const field = category.getField(k);
  73. cache[k] = !!field ? ctor(field, category, k) : Column.Undefined(category.rowCount, fType);
  74. }
  75. return cache[k];
  76. },
  77. enumerable: true,
  78. configurable: false
  79. });
  80. }
  81. }
  82. }
  83. function createDatabase(schema: Database.Schema, frame: Data.Frame): Database<any> {
  84. const tables = Object.create(null);
  85. for (const k of Object.keys(schema)) {
  86. tables[k] = createTable(k, (schema as any)[k], frame);
  87. }
  88. return Database.ofTables(frame.header, schema, tables);
  89. }
  90. function createTable(key: string, schema: Table.Schema, frame: Data.Frame) {
  91. const cat = frame.categories[key];
  92. return new CategoryTable(cat || Data.Category.empty(key), schema, !!cat);
  93. }