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

[EPM] Add mapping field types to index template generation #59894

Closed
wants to merge 11 commits into from

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
ElasticsearchAssetType,
} from '../../../../types';
import { CallESAsCurrentUser } from '../../../../types';
import { Field, loadFieldsFromYaml } from '../../fields/field';
import { Field, loadFieldsFromYaml, processFields } from '../../fields/field';
import { getPipelineNameForInstallation } from '../ingest_pipeline/install';
import { generateMappings, generateTemplateName, getTemplate } from './template';
import * as Registry from '../../registry';
Expand Down Expand Up @@ -98,7 +98,7 @@ export async function installTemplate({
dataset: Dataset;
packageVersion: string;
}): Promise<AssetReference> {
const mappings = generateMappings(fields);
const mappings = generateMappings(processFields(fields));
const templateName = generateTemplateName(dataset);
let pipelineName;
if (dataset.ingest_pipeline) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,38 @@ test('get template', () => {
expect(template.index_patterns).toStrictEqual([`${templateName}-*`]);
});

test('tests loading fields.yml', () => {
// Load fields.yml file
test('tests loading base.yml', () => {
const ymlPath = path.join(__dirname, '../../fields/tests/base.yml');
const fieldsYML = readFileSync(ymlPath, 'utf-8');
const fields: Field[] = safeLoad(fieldsYML);

processFields(fields);
const mappings = generateMappings(fields);
const processedFields = processFields(fields);
const mappings = generateMappings(processedFields);
const template = getTemplate('logs', 'foo', mappings);

expect(template).toMatchSnapshot(path.basename(ymlPath));
});

test('tests loading coredns.logs.yml', () => {
const ymlPath = path.join(__dirname, '../../fields/tests/coredns.logs.yml');
const fieldsYML = readFileSync(ymlPath, 'utf-8');
const fields: Field[] = safeLoad(fieldsYML);

const processedFields = processFields(fields);
const mappings = generateMappings(processedFields);
const template = getTemplate('logs', 'foo', mappings);

expect(template).toMatchSnapshot(path.basename(ymlPath));
});

test('tests loading system.yml', () => {
const ymlPath = path.join(__dirname, '../../fields/tests/system.yml');
const fieldsYML = readFileSync(ymlPath, 'utf-8');
const fields: Field[] = safeLoad(fieldsYML);

const processedFields = processFields(fields);
const mappings = generateMappings(processedFields);
const template = getTemplate('metrics', 'whatsthis', mappings);

expect(template).toMatchSnapshot(path.basename(ymlPath));
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ interface Properties {
interface Mappings {
properties: any;
}

const DEFAULT_SCALING_FACTOR = 1000;
const DEFAULT_IGNORE_ABOVE = 1024;

/**
* getTemplate retrieves the default template but overwrites the index pattern with the given value.
*
Expand All @@ -33,31 +37,92 @@ export function getTemplate(
}

/**
* Generate mapping takes the given fields array and creates the Elasticsearch
* Generate mapping takes the given nested fields array and creates the Elasticsearch
* mapping properties out of it.
*
* This assumes that all fields with dotted.names have been expanded in a previous step.
*
* @param fields
*/
export function generateMappings(fields: Field[]): Mappings {
const props: Properties = {};
fields.forEach(field => {
// Are there more fields inside this field? Build them recursively
if (field.fields && field.fields.length > 0) {
props[field.name] = generateMappings(field.fields);
return;
}

// If not type is defined, take keyword
// If type is not defined, assume keyword
const type = field.type || 'keyword';
// Only add keyword fields for now
// TODO: add support for other field types
if (type === 'keyword') {
props[field.name] = { type };

let fieldProps = getDefaultProperties(field);

switch (type) {
case 'group':
fieldProps = generateMappings(field.fields!);
break;
case 'integer':
fieldProps.type = 'long';
break;
case 'scaled_float':
fieldProps.type = 'scaled_float';
fieldProps.scaling_factor = field.scaling_factor || DEFAULT_SCALING_FACTOR;
break;
case 'text':
fieldProps.type = 'text';
if (field.analyzer) {
fieldProps.analyzer = field.analyzer;
}
if (field.search_analyzer) {
fieldProps.search_analyzer = field.search_analyzer;
}
break;
case 'keyword':
fieldProps.type = 'keyword';
if (field.ignore_above) {
fieldProps.ignore_above = field.ignore_above;
} else {
fieldProps.ignore_above = DEFAULT_IGNORE_ABOVE;
}
break;
// TODO move handling of multi_fields here?
case 'object':
// TODO improve
fieldProps.type = 'object';
break;
case 'array':
// TODO what happens when object_type is not set? beats implementation unclear
if (field.object_type) {
fieldProps.type = field.object_type;
}
break;
case 'alias':
// this assumes alias fields were validated in an earlier step
// adding a path to a field that doesn't exist would result in an error
// when the template is added to ES.
fieldProps.type = 'alias';
fieldProps.path = field.path;
break;
default:
fieldProps.type = type;
}
props[field.name] = fieldProps;
});

return { properties: props };
}

function getDefaultProperties(field: Field): Properties {
const properties: Properties = {};

if (field.index) {
properties.index = field.index;
}
if (field.doc_values) {
properties.doc_values = field.doc_values;
}
if (field.copy_to) {
properties.copy_to = field.copy_to;
}

return properties;
}

/**
* Generates the template name out of the given information
*/
Expand Down
Loading