Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Field list - from indexed array to arrays and maps #47921

Merged
merged 21 commits into from
Oct 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we loose extends Array as thats already part of FieldListInterface definition ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears to be needed. Seems that FieldList doesn't inherit that trait from 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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see .. nice :)

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