import {IRecordTransformer} from './record-transformer.interface'; import { RecordCollection, ISerializedRecordCollection } from '../record-collection'; import _ from 'lodash'; export class EsSearchRecordTransformService implements IRecordTransformer { transform( rawData: IEsSearchHit[], opts: ISerializedRecordCollection = {}): RecordCollection { const data = this.extractNestedAttributes(rawData); const columns = this.parseColumns(data); const records = this.parseRecords(data, opts); return new RecordCollection( _.defaults( { fields: columns, records: records, geoTransformStrategy: 'getOwnReference' }, opts ) ); } private parseColumns(data: IEsSearchHit[]) { const firstHit = data[0] ? data[0]._source : undefined; return firstHit ? _.reduce(firstHit, this.reduceHitToColumns.bind(this), []) : []; } private reduceHitToColumns(acc: IEsSearchResultColumn[], value, key) : IEsSearchResultColumn[] { return !_.isArray(value) ? this.addColTo(acc, key) : acc; } private addColTo(acc: IEsSearchResultColumn[], key: any) { const label = this.resolveLabel(key); return acc.concat({ name: key, label: label, visible: key === 'geoJson' ? false : true }); } private resolveLabel(key): string { const cumRegex = /([Cc])um/; return _.startCase(key).replace(cumRegex, '$1umulative'); } private parseRecords( data: IEsSearchHit[], opts: ISerializedRecordCollection) : any { return _(data) .map('_source') .map(source => this.filterFlatRecords(source)) .map(record => this.addMapMetadata(record, opts)) .value(); } private filterFlatRecords(source: any): any { return _.pick(source, value => !_.isArray(value)); } private extractNestedAttributes(rawData: IEsSearchHit[]): any { return _.map(rawData, (data: IEsSearchHit) => { const source = data._source; if (source.qualifyTaxRates[0]) { source.leaseName = source.qualifyTaxRates[0].leaseName; source.leaseNumber = source.leaseName; } const operators = source.operators; if (operators[0]) { source['operator_names'] = operators[0].companyName; } return data; }); } private addMapMetadata(record: any, opts: ISerializedRecordCollection): any { // all of this is a pretty ugly bandaid over having to bridge between // data from es & ows and what attributes the map expects to be present in // order to map the data. Ideally, once we rely fully on elastic search // we can just alter the DocRenderSettings class in atla-map to change // what the map expects when applying styles. const date = new Date(record.firstReportedMonth).toISOString().slice(0, 10); record['timespan'] = {begin: date}; record['record-type'] = opts.name; record['cumulative_oil'] = record.oilCum; record['cumulative_gas'] = record.gasCum; return record; } } export interface IEsSearchHit { _id: string; _index: string; _score: number; _source: any; _type: string; } export interface IEsSearchResultColumn { name: string; label: string; visible: boolean; }