query.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /**
  2. * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { Column } from '../../../mol-data/db';
  7. import { CifWriter } from '../../../mol-io/writer/cif';
  8. import { StructureQuery, StructureSelection, Structure } from '../../../mol-model/structure';
  9. import { encode_mmCIF_categories } from '../../../mol-model/structure/export/mmcif';
  10. import { Progress } from '../../../mol-task';
  11. import { now } from '../../../mol-util/now';
  12. import { ConsoleLogger } from '../../../mol-util/console-logger';
  13. import { PerformanceMonitor } from '../../../mol-util/performance-monitor';
  14. import Config from '../config';
  15. import Version from '../version';
  16. import { Job } from './jobs';
  17. import { createStructureWrapperFromJob, StructureWrapper, resolveStructures } from './structure-wrapper';
  18. import CifField = CifWriter.Field
  19. import { createModelPropertiesProviderFromConfig, ModelPropertiesProvider } from '../property-provider';
  20. export interface Stats {
  21. structure: StructureWrapper,
  22. queryTimeMs: number,
  23. encodeTimeMs: number,
  24. resultSize: number
  25. }
  26. const perf = new PerformanceMonitor();
  27. let _propertyProvider: ModelPropertiesProvider;
  28. function propertyProvider() {
  29. if (_propertyProvider) return _propertyProvider;
  30. _propertyProvider = createModelPropertiesProviderFromConfig() || (() => []);
  31. return _propertyProvider;
  32. }
  33. export async function resolveJob(job: Job): Promise<CifWriter.Encoder<any>> {
  34. ConsoleLogger.logId(job.id, 'Query', 'Starting.');
  35. const wrappedStructure = await createStructureWrapperFromJob(job, propertyProvider());
  36. try {
  37. perf.start('query');
  38. const sourceStructures = await resolveStructures(wrappedStructure, job.modelNums);
  39. if (!sourceStructures.length) throw new Error('Model not available');
  40. let structures: Structure[] = sourceStructures;
  41. if (job.queryDefinition.structureTransform) {
  42. structures = [];
  43. for (const s of sourceStructures) {
  44. structures.push(await job.queryDefinition.structureTransform(job.normalizedParams, s));
  45. }
  46. }
  47. const queries = structures.map(s => job.queryDefinition.query(job.normalizedParams, s));
  48. const result: Structure[] = [];
  49. for (let i = 0; i < structures.length; i++) {
  50. const s = await StructureSelection.unionStructure(StructureQuery.run(queries[i], structures[i], Config.maxQueryTimeInMs))
  51. if (s.elementCount > 0) result.push(s);
  52. }
  53. perf.end('query');
  54. const encoder = CifWriter.createEncoder({
  55. binary: job.responseFormat.isBinary,
  56. encoderName: `ModelServer ${Version}`,
  57. binaryEncodingPovider: getEncodingProvider(wrappedStructure),
  58. binaryAutoClassifyEncoding: true
  59. });
  60. ConsoleLogger.logId(job.id, 'Query', 'Query finished.');
  61. perf.start('encode');
  62. encoder.startDataBlock(sourceStructures[0].models[0].label.toUpperCase());
  63. encoder.writeCategory(_model_server_result, job);
  64. encoder.writeCategory(_model_server_params, job);
  65. if (job.queryDefinition.filter) encoder.setFilter(job.queryDefinition.filter);
  66. if (result.length > 0) encode_mmCIF_categories(encoder, result);
  67. if (job.queryDefinition.filter) encoder.setFilter();
  68. perf.end('encode');
  69. const stats: Stats = {
  70. structure: wrappedStructure,
  71. queryTimeMs: perf.time('query'),
  72. encodeTimeMs: perf.time('encode'),
  73. resultSize: result.reduce((n, s) => n + s.elementCount, 0)
  74. };
  75. encoder.writeCategory(_model_server_stats, stats);
  76. encoder.encode();
  77. ConsoleLogger.logId(job.id, 'Query', 'Encoded.');
  78. return encoder;
  79. } catch (e) {
  80. ConsoleLogger.errorId(job.id, e);
  81. return doError(job, e);
  82. }
  83. }
  84. function getEncodingProvider(structure: StructureWrapper) {
  85. if (!structure.isBinary) return void 0;
  86. return CifWriter.createEncodingProviderFromCifFrame(structure.cifFrame);
  87. }
  88. function doError(job: Job, e: any) {
  89. const encoder = CifWriter.createEncoder({ binary: job.responseFormat.isBinary, encoderName: `ModelServer ${Version}` });
  90. encoder.writeCategory(_model_server_result, job);
  91. encoder.writeCategory(_model_server_params, job);
  92. encoder.writeCategory(_model_server_error, '' + e);
  93. encoder.encode();
  94. return encoder;
  95. }
  96. const maxTime = Config.maxQueryTimeInMs;
  97. export function abortingObserver(p: Progress) {
  98. if (now() - p.root.progress.startedTime > maxTime) {
  99. p.requestAbort(`Exceeded maximum allowed time for a query (${maxTime}ms)`);
  100. }
  101. }
  102. function string<T>(name: string, str: (data: T, i: number) => string, isSpecified?: (data: T) => boolean): CifField<number, T> {
  103. if (isSpecified) {
  104. return CifField.str(name, (i, d) => str(d, i), { valueKind: (i, d) => isSpecified(d) ? Column.ValueKind.Present : Column.ValueKind.NotPresent });
  105. }
  106. return CifField.str(name, (i, d) => str(d, i));
  107. }
  108. function int32<T>(name: string, value: (data: T) => number): CifField<number, T> {
  109. return CifField.int(name, (i, d) => value(d));
  110. }
  111. const _model_server_result_fields: CifField<any, Job>[] = [
  112. string<Job>('job_id', ctx => '' + ctx.id),
  113. string<Job>('datetime_utc', ctx => ctx.datetime_utc),
  114. string<Job>('server_version', ctx => Version),
  115. string<Job>('query_name', ctx => ctx.queryDefinition.name),
  116. string<Job>('source_id', ctx => ctx.sourceId),
  117. string<Job>('entry_id', ctx => ctx.entryId),
  118. ];
  119. const _model_server_params_fields: CifField<number, string[]>[] = [
  120. string<string[]>('name', (ctx, i) => ctx[i][0]),
  121. string<string[]>('value', (ctx, i) => ctx[i][1])
  122. ];
  123. const _model_server_error_fields: CifField<number, string>[] = [
  124. string<string>('message', (ctx, i) => ctx)
  125. ];
  126. const _model_server_stats_fields: CifField<number, Stats>[] = [
  127. int32<Stats>('io_time_ms', ctx => ctx.structure.info.readTime | 0),
  128. int32<Stats>('parse_time_ms', ctx => ctx.structure.info.parseTime | 0),
  129. // int32<Stats>('attach_props_time_ms', ctx => ctx.structure.info.attachPropsTime | 0),
  130. int32<Stats>('create_model_time_ms', ctx => ctx.structure.info.createModelTime | 0),
  131. int32<Stats>('query_time_ms', ctx => ctx.queryTimeMs | 0),
  132. int32<Stats>('encode_time_ms', ctx => ctx.encodeTimeMs | 0),
  133. int32<Stats>('element_count', ctx => ctx.resultSize | 0),
  134. ];
  135. const _model_server_result: CifWriter.Category<Job> = {
  136. name: 'model_server_result',
  137. instance: (job) => CifWriter.categoryInstance(_model_server_result_fields, { data: job, rowCount: 1 })
  138. };
  139. const _model_server_error: CifWriter.Category<string> = {
  140. name: 'model_server_error',
  141. instance: (message) => CifWriter.categoryInstance(_model_server_error_fields, { data: message, rowCount: 1 })
  142. };
  143. const _model_server_params: CifWriter.Category<Job> = {
  144. name: 'model_server_params',
  145. instance(job) {
  146. const params: string[][] = [];
  147. for (const k of Object.keys(job.normalizedParams)) {
  148. params.push([k, JSON.stringify(job.normalizedParams[k])]);
  149. }
  150. return CifWriter.categoryInstance(_model_server_params_fields, { data: params, rowCount: params.length });
  151. }
  152. };
  153. const _model_server_stats: CifWriter.Category<Stats> = {
  154. name: 'model_server_stats',
  155. instance: (stats) => CifWriter.categoryInstance(_model_server_stats_fields, { data: stats, rowCount: 1 })
  156. }