query.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { UUID } from 'mol-util';
  7. import { getQueryByName, normalizeQueryParams, QueryDefinition } from './api';
  8. import { getStructure, StructureWrapper } from './structure-wrapper';
  9. import Config from '../config';
  10. import { Progress, now } from 'mol-task';
  11. import { ConsoleLogger } from 'mol-util/console-logger';
  12. import Writer from 'mol-io/writer/writer';
  13. import { CifWriter } from 'mol-io/writer/cif'
  14. import { encode_mmCIF_categories } from 'mol-model/structure/export/mmcif';
  15. import { Selection } from 'mol-model/structure';
  16. import Version from '../version'
  17. import { Column } from 'mol-data/db';
  18. import { PerformanceMonitor } from 'mol-util/performance-monitor';
  19. export interface ResponseFormat {
  20. isBinary: boolean
  21. }
  22. export interface Request {
  23. id: UUID,
  24. datetime_utc: string,
  25. sourceId: '_local_' | string,
  26. entryId: string,
  27. queryDefinition: QueryDefinition,
  28. normalizedParams: any,
  29. responseFormat: ResponseFormat
  30. }
  31. export interface Stats {
  32. structure: StructureWrapper,
  33. queryTimeMs: number,
  34. encodeTimeMs: number
  35. }
  36. export function createRequest(sourceId: '_local_' | string, entryId: string, queryName: string, params: any): Request {
  37. const queryDefinition = getQueryByName(queryName);
  38. if (!queryDefinition) throw new Error(`Query '${queryName}' is not supported.`);
  39. const normalizedParams = normalizeQueryParams(queryDefinition, params);
  40. return {
  41. id: UUID.create(),
  42. datetime_utc: `${new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')}`,
  43. sourceId,
  44. entryId,
  45. queryDefinition,
  46. normalizedParams,
  47. responseFormat: { isBinary: !!params.binary }
  48. };
  49. }
  50. const perf = new PerformanceMonitor();
  51. export async function resolveRequest(req: Request, writer: Writer) {
  52. ConsoleLogger.logId(req.id, 'Query', 'Starting.');
  53. const wrappedStructure = await getStructure(req.sourceId, req.entryId);
  54. perf.start('query');
  55. const structure = req.queryDefinition.structureTransform
  56. ? await req.queryDefinition.structureTransform(req.normalizedParams, wrappedStructure.structure)
  57. : wrappedStructure.structure;
  58. const query = req.queryDefinition.query(req.normalizedParams, structure);
  59. const result = Selection.unionStructure(await query(structure).run(abortingObserver, 250));
  60. perf.end('query');
  61. ConsoleLogger.logId(req.id, 'Query', 'Query finished.');
  62. const encoder = CifWriter.createEncoder({ binary: req.responseFormat.isBinary, encoderName: `ModelServer ${Version}` });
  63. perf.start('encode');
  64. encoder.startDataBlock(structure.units[0].model.label.toUpperCase());
  65. encoder.writeCategory(_model_server_result, [req]);
  66. encoder.writeCategory(_model_server_params, [req]);
  67. // encoder.setFilter(mmCIF_Export_Filters.onlyPositions);
  68. encode_mmCIF_categories(encoder, result);
  69. // encoder.setFilter();
  70. perf.end('encode');
  71. ConsoleLogger.logId(req.id, 'Query', 'Encoded.');
  72. const stats: Stats = {
  73. structure: wrappedStructure,
  74. queryTimeMs: perf.time('query'),
  75. encodeTimeMs: perf.time('encode')
  76. };
  77. encoder.writeCategory(_model_server_stats, [stats]);
  78. encoder.encode();
  79. encoder.writeTo(writer);
  80. ConsoleLogger.logId(req.id, 'Query', 'Written.');
  81. }
  82. const maxTime = Config.maxQueryTimeInMs;
  83. export function abortingObserver(p: Progress) {
  84. if (now() - p.root.progress.startedTime > maxTime) {
  85. p.requestAbort(`Exceeded maximum allowed time for a query (${maxTime}ms)`);
  86. }
  87. }
  88. import CifField = CifWriter.Field
  89. function string<T>(name: string, str: (data: T, i: number) => string, isSpecified?: (data: T) => boolean): CifField<number, T> {
  90. if (isSpecified) {
  91. return CifField.str(name, (i, d) => str(d, i), { valueKind: (i, d) => isSpecified(d) ? Column.ValueKind.Present : Column.ValueKind.NotPresent });
  92. }
  93. return CifField.str(name, (i, d) => str(d, i));
  94. }
  95. function int32<T>(name: string, value: (data: T) => number): CifField<number, T> {
  96. return CifField.int(name, (i, d) => value(d));
  97. }
  98. const _model_server_result_fields: CifField<number, Request>[] = [
  99. string<Request>('request_id', ctx => '' + ctx.id),
  100. string<Request>('datetime_utc', ctx => ctx.datetime_utc),
  101. string<Request>('server_version', ctx => Version),
  102. string<Request>('query_name', ctx => ctx.queryDefinition.name),
  103. string<Request>('source_id', ctx => ctx.sourceId),
  104. string<Request>('entry_id', ctx => ctx.entryId),
  105. ];
  106. const _model_server_params_fields: CifField<number, string[]>[] = [
  107. string<string[]>('name', (ctx, i) => ctx[i][0]),
  108. string<string[]>('value', (ctx, i) => ctx[i][1])
  109. ];
  110. const _model_server_stats_fields: CifField<number, Stats>[] = [
  111. int32<Stats>('io_time_ms', ctx => ctx.structure.info.readTime | 0),
  112. int32<Stats>('parse_time_ms', ctx => ctx.structure.info.parseTime | 0),
  113. int32<Stats>('create_model_time_ms', ctx => ctx.structure.info.createModelTime | 0),
  114. int32<Stats>('query_time_ms', ctx => ctx.queryTimeMs | 0),
  115. int32<Stats>('encode_time_ms', ctx => ctx.encodeTimeMs | 0)
  116. ];
  117. const _model_server_result: CifWriter.Category<Request> = {
  118. name: 'model_server_result',
  119. instance: (request) => ({ data: request, fields: _model_server_result_fields, rowCount: 1 })
  120. };
  121. const _model_server_params: CifWriter.Category<Request> = {
  122. name: 'model_server_params',
  123. instance(request) {
  124. const params: string[][] = [];
  125. for (const k of Object.keys(request.normalizedParams)) {
  126. params.push([k, '' + request.normalizedParams[k]]);
  127. }
  128. return {
  129. data: params,
  130. fields: _model_server_params_fields,
  131. rowCount: params.length
  132. }
  133. }
  134. };
  135. const _model_server_stats: CifWriter.Category<Stats> = {
  136. name: 'model_server_stats',
  137. instance: (stats) => ({ data: stats, fields: _model_server_stats_fields, rowCount: 1 })
  138. }