Skip to content

Commit

Permalink
Field list - from indexed array to arrays and maps (#47921)
Browse files Browse the repository at this point in the history
* make fieldList extend array
  • Loading branch information
mattkime authored Oct 14, 2019
1 parent 4a7f36d commit b92faf2
Show file tree
Hide file tree
Showing 49 changed files with 247 additions and 170 deletions.
8 changes: 4 additions & 4 deletions src/fixtures/fake_row.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
* under the License.
*/

import _ from 'lodash';
const longString = Array(200).join('_');

export function getFakeRowVals(type, id, mapping) {
return _.mapValues(mapping, function (f, c) {
return c + '_' + type + '_' + id + longString;
});
return mapping.reduce((collector, field) => {
collector[field.name] = `${field.name}_${type}_${id}_${longString}`;
return collector;
}, {});
}

export function getFakeRow(id, mapping) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import { Filter, RangeFilter, FILTERS, isRangeFilter, isScriptedRangeFilter } from '@kbn/es-query';
import { get, has } from 'lodash';
import { SavedObjectNotFound } from '../../../../../../../plugins/kibana_utils/public';
import { IndexPatterns, IndexPattern } from '../../../index_patterns';
import { IndexPatterns, IndexPattern, Field } from '../../../index_patterns';

const getFirstRangeKey = (filter: RangeFilter) => filter.range && Object.keys(filter.range)[0];
const getRangeByKey = (filter: RangeFilter, key: string) => get(filter, ['range', key]);
Expand All @@ -44,9 +44,8 @@ function getParams(filter: RangeFilter, indexPattern?: IndexPattern) {
// for example a user might manually edit the url or the index pattern's ID might change due to
// external factors e.g. a reindex. We only need the index in order to grab the field formatter, so we fallback
// on displaying the raw value if the index is invalid.
if (key && indexPattern && indexPattern.fields.byName[key]) {
const convert = indexPattern.fields.byName[key].format.getConverterFor('text');

if (key && indexPattern && indexPattern.fields.getByName(key)) {
const convert = (indexPattern.fields.getByName(key) as Field).format.getConverterFor('text');
value = `${convert(left)} to ${convert(right)}`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class StubIndexPatterns {
async get(index: string) {
return {
fields: {
byName: {},
getByName: () => undefined,
},
};
}
Expand Down
1 change: 1 addition & 0 deletions src/legacy/core_plugins/data/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export { FilterBar, ApplyFiltersPopover } from './filter';
export {
Field,
FieldType,
FieldListInterface,
IndexPattern,
IndexPatterns,
StaticIndexPattern,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,69 @@
* under the License.
*/

import { IndexedArray } from 'ui/indexed_array';
import { NotificationsSetup } from 'kibana/public';
import { findIndex } from 'lodash';
import { IndexPattern } from '../index_patterns';
import { Field, FieldSpec } from './field';
import { Field, FieldType, FieldSpec } from './field';

export class FieldList extends IndexedArray<Field> {
type FieldMap = Map<Field['name'], Field>;

export interface FieldListInterface extends Array<Field> {
getByName(name: Field['name']): Field | undefined;
getByType(type: Field['type']): Field[];
add(field: FieldSpec): void;
remove(field: FieldType): void;
}

export class FieldList extends Array<Field> implements FieldListInterface {
private byName: FieldMap = new Map();
private groups: Map<Field['type'], FieldMap> = new Map();
private indexPattern: IndexPattern;
private shortDotsEnable: boolean;
private notifications: NotificationsSetup;
private setByName = (field: Field) => this.byName.set(field.name, field);
private setByGroup = (field: Field) => {
if (typeof this.groups.get(field.type) === 'undefined') {
this.groups.set(field.type, new Map());
}
this.groups.get(field.type)!.set(field.name, field);
};
private removeByGroup = (field: FieldType) => this.groups.get(field.type)!.delete(field.name);
constructor(
indexPattern: IndexPattern,
specs: FieldSpec[],
specs: FieldSpec[] = [],
shortDotsEnable = false,
notifications: NotificationsSetup
) {
super({
index: ['name'],
group: ['type'],
initialSet: specs.map(function(field) {
return new Field(indexPattern, field, shortDotsEnable, notifications);
}),
});
super();
this.indexPattern = indexPattern;
this.shortDotsEnable = shortDotsEnable;
this.notifications = notifications;
specs.map(field => this.add(field));
}

getByName = (name: Field['name']) => this.byName.get(name);
getByType = (type: Field['type']) => [...(this.groups.get(type) || new Map()).values()];
add = (field: FieldSpec) => {
const newField = new Field(this.indexPattern, field, this.shortDotsEnable, this.notifications);
this.push(newField);
this.setByName(newField);
this.setByGroup(newField);
};

remove = (field: FieldType) => {
this.removeByGroup(field);
this.byName.delete(field.name);

const fieldIndex = findIndex(this, { name: field.name });
this.splice(fieldIndex, 1);
};

update = (field: Field) => {
const index = this.findIndex(f => f.name === field.name);
this.splice(index, 1, field);
this.setByName(field);
this.removeByGroup(field);
this.setByGroup(field);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ function flattenHit(indexPattern: IndexPattern, hit: Record<string, any>, deep:
const flat = {} as Record<string, any>;

// recursively merge _source
const fields = indexPattern.fields.byName;
const fields = indexPattern.fields.getByName;
(function flatten(obj, keyPrefix = '') {
keyPrefix = keyPrefix ? keyPrefix + '.' : '';
_.forOwn(obj, function(val, key) {
key = keyPrefix + key;

if (deep) {
const isNestedField = fields[key] && fields[key].type === 'nested';
const field = fields(key);
const isNestedField = field && field.type === 'nested';
const isArrayOfObjects = Array.isArray(val) && _.isPlainObject(_.first(val));
if (isArrayOfObjects && !isNestedField) {
_.each(val, v => flatten(v, key));
Expand All @@ -44,7 +45,8 @@ function flattenHit(indexPattern: IndexPattern, hit: Record<string, any>, deep:
return;
}

const hasValidMapping = fields[key] && fields[key].type !== 'conflict';
const field = fields(key);
const hasValidMapping = field && field.type !== 'conflict';
const isValue = !_.isPlainObject(val);

if (hasValidMapping || isValue) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const partialFormattedCache = new WeakMap();
// returns a formatted version
export function formatHitProvider(indexPattern: IndexPattern, defaultFormat: any) {
function convert(hit: Record<string, any>, val: any, fieldName: string, type: string = 'html') {
const field = indexPattern.fields.byName[fieldName];
const field = indexPattern.fields.getByName(fieldName);
if (!field) return defaultFormat.convert(val, type);
const parsedUrl = {
origin: window.location.origin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/

import { defaults, pluck, last, get } from 'lodash';
import { IndexedArray } from 'ui/indexed_array';
import { IndexPattern } from './index_pattern';

import { DuplicateField } from '../../../../../../plugins/kibana_utils/public';
Expand Down Expand Up @@ -170,7 +169,6 @@ describe('IndexPattern', () => {
test('should append the found fields', () => {
expect(savedObjectsClient.get).toHaveBeenCalled();
expect(indexPattern.fields).toHaveLength(mockLogStashFields().length);
expect(indexPattern.fields).toBeInstanceOf(IndexedArray);
});
});

Expand Down Expand Up @@ -295,7 +293,9 @@ describe('IndexPattern', () => {
const scriptedFields = indexPattern.getScriptedFields();
// expect(saveSpy.callCount).to.equal(1);
expect(scriptedFields).toHaveLength(oldCount + 1);
expect(indexPattern.fields.byName[scriptedField.name].name).toEqual(scriptedField.name);
expect((indexPattern.fields.getByName(scriptedField.name) as Field).name).toEqual(
scriptedField.name
);
});

test('should remove scripted field, by name', async () => {
Expand All @@ -304,11 +304,11 @@ describe('IndexPattern', () => {
const oldCount = scriptedFields.length;
const scriptedField = last(scriptedFields);

await indexPattern.removeScriptedField(scriptedField.name);
await indexPattern.removeScriptedField(scriptedField);

// expect(saveSpy.callCount).to.equal(1);
expect(indexPattern.getScriptedFields().length).toEqual(oldCount - 1);
expect(indexPattern.fields.byName[scriptedField.name]).toEqual(undefined);
expect(indexPattern.fields.getByName(scriptedField.name)).toEqual(undefined);
});

test('should not allow duplicate names', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { NotificationsSetup, SavedObjectsClientContract } from 'src/core/public'
import { SavedObjectNotFound, DuplicateField } from '../../../../../../plugins/kibana_utils/public';

import { IndexPatternMissingIndices } from '../errors';
import { Field, FieldList, FieldType } from '../fields';
import { Field, FieldList, FieldType, FieldListInterface } from '../fields';
import { createFieldsFetcher } from './_fields_fetcher';
import { getRoutes } from '../utils';
import { formatHitProvider } from './format_hit';
Expand Down Expand Up @@ -63,7 +63,7 @@ export class IndexPattern implements StaticIndexPattern {
public type?: string;
public fieldFormatMap: any;
public typeMeta: any;
public fields: FieldList;
public fields: FieldListInterface;
public timeFieldName: string | undefined;
public formatHit: any;
public formatField: any;
Expand Down Expand Up @@ -211,15 +211,17 @@ export class IndexPattern implements StaticIndexPattern {
// Date value returned in "_source" could be in any number of formats
// Use a docvalue for each date field to ensure standardized formats when working with date fields
// indexPattern.flattenHit will override "_source" values when the same field is also defined in "fields"
const docvalueFields = reject(this.fields.byType.date, 'scripted').map((dateField: any) => {
return {
field: dateField.name,
format:
dateField.esTypes && dateField.esTypes.indexOf('date_nanos') !== -1
? 'strict_date_time'
: 'date_time',
};
});
const docvalueFields = reject(this.fields.getByType('date'), 'scripted').map(
(dateField: any) => {
return {
field: dateField.name,
format:
dateField.esTypes && dateField.esTypes.indexOf('date_nanos') !== -1
? 'strict_date_time'
: 'date_time',
};
}
);

each(this.getScriptedFields(), function(field) {
scriptFields[field.name] = {
Expand Down Expand Up @@ -275,7 +277,7 @@ export class IndexPattern implements StaticIndexPattern {
throw new DuplicateField(name);
}

this.fields.push(
this.fields.add(
new Field(
this,
{
Expand All @@ -296,21 +298,13 @@ export class IndexPattern implements StaticIndexPattern {
await this.save();
}

removeScriptedField(name: string) {
const fieldIndex = _.findIndex(this.fields, {
name,
scripted: true,
});

if (fieldIndex > -1) {
this.fields.splice(fieldIndex, 1);
delete this.fieldFormatMap[name];
return this.save();
}
removeScriptedField(field: FieldType) {
this.fields.remove(field);
return this.save();
}

async popularizeField(fieldName: string, unit = 1) {
const field = this.fields.byName[fieldName];
const field = this.fields.getByName(fieldName);
if (!field) {
return;
}
Expand Down Expand Up @@ -344,13 +338,13 @@ export class IndexPattern implements StaticIndexPattern {
}

getTimeField() {
if (!this.timeFieldName || !this.fields || !this.fields.byName) return;
return this.fields.byName[this.timeFieldName];
if (!this.timeFieldName || !this.fields || !this.fields.getByName) return;
return this.fields.getByName(this.timeFieldName);
}

getFieldByName(name: string): Field | void {
if (!this.fields || !this.fields.byName) return;
return this.fields.byName[name];
if (!this.fields || !this.fields.getByName) return;
return this.fields.getByName(name);
}

isWildcard() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
HttpServiceBase,
NotificationsSetup,
} from 'src/core/public';
import { Field, FieldList, FieldType } from './fields';
import { Field, FieldList, FieldListInterface, FieldType } from './fields';
import { createFlattenHitWrapper } from './index_patterns';
import { createIndexPatternSelect } from './components';
import {
Expand Down Expand Up @@ -107,4 +107,4 @@ export type IndexPatternsSetup = ReturnType<IndexPatternsService['setup']>;
export type IndexPatternsStart = ReturnType<IndexPatternsService['start']>;

/** @public */
export { IndexPattern, IndexPatterns, StaticIndexPattern, Field, FieldType };
export { IndexPattern, IndexPatterns, StaticIndexPattern, Field, FieldType, FieldListInterface };
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class FilterManager {
}

getField() {
return this.indexPattern.fields.byName[this.fieldName];
return this.indexPattern.fields.getByName(this.fieldName);
}

createFilter() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ export class PhraseFilterManager extends FilterManager {
let newFilter;
if (phrases.length === 1) {
newFilter = buildPhraseFilter(
this.indexPattern.fields.byName[this.fieldName],
this.indexPattern.fields.getByName(this.fieldName),
phrases[0],
this.indexPattern);
} else {
newFilter = buildPhrasesFilter(
this.indexPattern.fields.byName[this.fieldName],
this.indexPattern.fields.getByName(this.fieldName),
phrases,
this.indexPattern);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ describe('PhraseFilterManager', function () {
const indexPatternMock = {
id: indexPatternId,
fields: {
byName: {
field1: fieldMock
getByName: name => {
const fields = { field1: fieldMock };
return fields[name];
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class RangeFilterManager extends FilterManager {
*/
createFilter(value) {
const newFilter = buildRangeFilter(
this.indexPattern.fields.byName[this.fieldName],
this.indexPattern.fields.getByName(this.fieldName),
toRange(value),
this.indexPattern);
newFilter.meta.key = this.fieldName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@ describe('RangeFilterManager', function () {
const indexPatternMock = {
id: indexPatternId,
fields: {
byName: {
field1: fieldMock
getByName: name => {
const fields = {
field1: fieldMock
};
return fields[name];
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class ListControl extends Control {
terminate_after: chrome.getInjected('autocompleteTerminateAfter')
};
const aggs = termsAgg({
field: indexPattern.fields.byName[fieldName],
field: indexPattern.fields.getByName(fieldName),
size: this.options.dynamicOptions ? null : _.get(this.options, 'size', 5),
direction: 'desc',
query
Expand Down
Loading

0 comments on commit b92faf2

Please sign in to comment.