Skip to content

Commit

Permalink
flattenHit: support object in array
Browse files Browse the repository at this point in the history
  • Loading branch information
scampi committed Dec 10, 2016
1 parent ef95ceb commit b64f92a
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 27 deletions.
55 changes: 43 additions & 12 deletions src/ui/public/index_patterns/__tests__/flatten_hit.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@ import ngMock from 'ng_mock';
import IndexPatternsFlattenHitProvider from 'ui/index_patterns/_flatten_hit';

describe('IndexPattern#flattenHit()', function () {


let flattenHit;
let config;
let hit;
let flat;

beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private, $injector) {
let indexPattern = {
fields: {
byName: {
'tags.text': { type: 'string' },
'tags.label': { type: 'string' },
'message': { type: 'string' },
'geo.coordinates': { type: 'geo_point' },
'geo.dest': { type: 'string' },
Expand All @@ -33,7 +32,12 @@ describe('IndexPattern#flattenHit()', function () {
}
};

flattenHit = Private(IndexPatternsFlattenHitProvider)(indexPattern).uncached;
const cachedFlatten = Private(IndexPatternsFlattenHitProvider)(indexPattern);
flattenHit = function (hit, deep = false) {
delete hit.$$_flattened;
return cachedFlatten(hit, deep);
};

config = $injector.get('config');

hit = {
Expand All @@ -46,7 +50,10 @@ describe('IndexPattern#flattenHit()', function () {
},
bytes: 10039103,
'@timestamp': (new Date()).toString(),
tags: [{ text: 'foo' }, { text: 'bar' }],
tags: [
{ text: 'foo', label: [ 'FOO1', 'FOO2' ] },
{ text: 'bar', label: 'BAR' }
],
groups: ['loners'],
noMapping: true,
team: [
Expand All @@ -61,11 +68,11 @@ describe('IndexPattern#flattenHit()', function () {
random: [0.12345]
}
};

flat = flattenHit(hit);
}));

it('flattens keys as far down as the mapping goes', function () {
const flat = flattenHit(hit);

expect(flat).to.have.property('geo.coordinates', hit._source.geo.coordinates);
expect(flat).to.not.have.property('geo.coordinates.lat');
expect(flat).to.not.have.property('geo.coordinates.lon');
Expand All @@ -77,22 +84,42 @@ describe('IndexPattern#flattenHit()', function () {
});

it('flattens keys not in the mapping', function () {
const flat = flattenHit(hit);

expect(flat).to.have.property('noMapping', true);
expect(flat).to.have.property('groups');
expect(flat.groups).to.eql(['loners']);
});

it('flattens conflicting types in the mapping', function () {
const flat = flattenHit(hit);

expect(flat).to.not.have.property('user');
expect(flat).to.have.property('user.name', hit._source.user.name);
expect(flat).to.have.property('user.id', hit._source.user.id);
});

it('preserves objects in arrays', function () {
it('should preserve objects in arrays if deep argument is false', function () {
const flat = flattenHit(hit);

expect(flat).to.have.property('tags', hit._source.tags);
});

it('should expand objects in arrays if deep argument is true', function () {
const flat = flattenHit(hit, true);

expect(flat['tags.text']).to.be.eql([ 'foo', 'bar' ]);
});

it('should support arrays when expanding objects in arrays if deep argument is true', function () {
const flat = flattenHit(hit, true);

expect(flat['tags.label']).to.be.eql([ 'FOO1', 'FOO2', 'BAR' ]);
});

it('does not enter into nested fields', function () {
const flat = flattenHit(hit);

expect(flat).to.have.property('team', hit._source.team);
expect(flat).to.not.have.property('team.name');
expect(flat).to.not.have.property('team.role');
Expand All @@ -101,32 +128,36 @@ describe('IndexPattern#flattenHit()', function () {
});

it('unwraps script fields', function () {
const flat = flattenHit(hit);

expect(flat).to.have.property('delta', 42);
});

it('assumes that all fields are "computed fields"', function () {
const flat = flattenHit(hit);

expect(flat).to.have.property('random', 0.12345);
});

it('ignores fields that start with an _ and are not in the metaFields', function () {
config.set('metaFields', ['_metaKey']);
hit.fields._notMetaKey = [100];
flat = flattenHit(hit);
const flat = flattenHit(hit);
expect(flat).to.not.have.property('_notMetaKey');
});

it('includes underscore-prefixed keys that are in the metaFields', function () {
config.set('metaFields', ['_metaKey']);
hit.fields._metaKey = [100];
flat = flattenHit(hit);
const flat = flattenHit(hit);
expect(flat).to.have.property('_metaKey', 100);
});

it('adapts to changes in the metaFields', function () {
hit.fields._metaKey = [100];

config.set('metaFields', ['_metaKey']);
flat = flattenHit(hit);
let flat = flattenHit(hit);
expect(flat).to.have.property('_metaKey', 100);

config.set('metaFields', []);
Expand All @@ -137,7 +168,7 @@ describe('IndexPattern#flattenHit()', function () {
it('handles fields that are not arrays, like _timestamp', function () {
hit.fields._metaKey = 20000;
config.set('metaFields', ['_metaKey']);
flat = flattenHit(hit);
const flat = flattenHit(hit);
expect(flat).to.have.property('_metaKey', 20000);
});
});
43 changes: 28 additions & 15 deletions src/ui/public/index_patterns/_flatten_hit.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import _ from 'lodash';

// Takes a hit, merges it with any stored/scripted fields, and with the metaFields
// returns a flattened version
export default function FlattenHitProvider(config) {
Expand All @@ -8,23 +9,40 @@ export default function FlattenHitProvider(config) {
metaFields = value;
});

function flattenHit(indexPattern, hit) {
let flat = {};
function flattenHit(indexPattern, hit, deep) {
const flat = {};

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

if (flat[key] !== void 0) return;
if (!deep && flat[key] !== void 0) {
return;
}

if (deep) {
const isNestedField = fields[key] && fields[key].type === 'nested';
const isArrayOfObjects = _.isArray(val) && _.isPlainObject(_.first(val));
if (isArrayOfObjects && !isNestedField) {
_.each(val, v => flatten(v, key));
return;
}
}

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

if (hasValidMapping || isValue) {
flat[key] = val;
if (!flat[key]) {
flat[key] = val;
} else if (_.isArray(flat[key])) {
flat[key].push(val);
} else {
flat[key] = [ flat[key], val ];
}
return;
}

Expand All @@ -48,13 +66,8 @@ export default function FlattenHitProvider(config) {
}

return function flattenHitWrapper(indexPattern) {
function cachedFlatten(hit) {
return hit.$$_flattened || (hit.$$_flattened = flattenHit(indexPattern, hit));
}

cachedFlatten.uncached = _.partial(flattenHit, indexPattern);

return cachedFlatten;
return function cachedFlatten(hit, deep = false) {
return hit.$$_flattened || (hit.$$_flattened = flattenHit(indexPattern, hit, deep));
};
};
};

0 comments on commit b64f92a

Please sign in to comment.