Metric matching support for search

Examples:

* cpu > 2 // means percent
* memory > 4.5MB
This commit is contained in:
David Kaltschmidt
2016-05-04 17:21:46 +02:00
parent d1609658bf
commit 3e26ed7083
3 changed files with 132 additions and 39 deletions

View File

@@ -20,7 +20,6 @@ function getHint(nodes) {
if (node.get('metadata')) {
const metadataField = node.get('metadata').first();
metadataLabel = slugify(metadataField.get('label'))
.toLowerCase()
.split(' ')[0]
.split('.').pop()
.substr(0, 20);

View File

@@ -1,4 +1,5 @@
import { Map as makeMap } from 'immutable';
import _ from 'lodash';
import { slugify } from './string-utils';
@@ -8,8 +9,29 @@ const SEARCH_FIELDS = makeMap({
sublabel: 'label_minor'
});
const COMPARISONS = makeMap({
'<': 'lt',
'>': 'gt',
'=': 'eq'
});
const COMPARISONS_REGEX = new RegExp(`[${COMPARISONS.keySeq().toJS().join('')}]`);
const PREFIX_DELIMITER = ':';
function parseValue(value) {
let parsed = parseFloat(value);
if (_.endsWith(value, 'KB')) {
parsed *= 1024;
} else if (_.endsWith(value, 'MB')) {
parsed *= 1024 * 1024;
} else if (_.endsWith(value, 'GB')) {
parsed *= 1024 * 1024 * 1024;
} else if (_.endsWith(value, 'TB')) {
parsed *= 1024 * 1024 * 1024 * 1024;
}
return parsed;
}
function matchPrefix(label, prefix) {
if (label && prefix) {
return (new RegExp(prefix, 'i')).test(slugify(label));
@@ -31,37 +53,86 @@ function findNodeMatch(nodeMatches, keyPath, text, query, prefix, label) {
return nodeMatches;
}
export function searchTopology(nodes, { prefix, query }) {
/**
* If the metric matches the field's label and the value compares positively
* with the comp operator, a nodeMatch is added
*/
function findNodeMatchMetric(nodeMatches, keyPath, fieldValue, fieldLabel, metric, comp, value) {
if (slugify(metric) === slugify(fieldLabel)) {
let matched = false;
switch (comp) {
case 'gt': {
if (fieldValue > value) {
matched = true;
}
break;
}
case 'lt': {
if (fieldValue < value) {
matched = true;
}
break;
}
case 'eq': {
if (fieldValue === value) {
matched = true;
}
break;
}
default: {
break;
}
}
if (matched) {
nodeMatches = nodeMatches.setIn(keyPath,
{fieldLabel, metric: true});
}
}
return nodeMatches;
}
export function searchTopology(nodes, { prefix, query, metric, comp, value }) {
let nodeMatches = makeMap();
nodes.forEach((node, nodeId) => {
// top level fields
SEARCH_FIELDS.forEach((field, label) => {
const keyPath = [nodeId, label];
nodeMatches = findNodeMatch(nodeMatches, keyPath, node.get(field),
query, prefix, label);
});
// metadata
if (node.get('metadata')) {
node.get('metadata').forEach(field => {
const keyPath = [nodeId, 'metadata', field.get('id')];
nodeMatches = findNodeMatch(nodeMatches, keyPath, field.get('value'),
query, prefix, field.get('label'));
if (query) {
// top level fields
SEARCH_FIELDS.forEach((field, label) => {
const keyPath = [nodeId, label];
nodeMatches = findNodeMatch(nodeMatches, keyPath, node.get(field),
query, prefix, label);
});
}
// tables (envvars and labels)
const tables = node.get('tables');
if (tables) {
tables.forEach((table) => {
if (table.get('rows')) {
table.get('rows').forEach(field => {
const keyPath = [nodeId, 'metadata', field.get('id')];
nodeMatches = findNodeMatch(nodeMatches, keyPath, field.get('value'),
query, prefix, field.get('label'));
});
}
});
// metadata
if (node.get('metadata')) {
node.get('metadata').forEach(field => {
const keyPath = [nodeId, 'metadata', field.get('id')];
nodeMatches = findNodeMatch(nodeMatches, keyPath, field.get('value'),
query, prefix, field.get('label'));
});
}
// tables (envvars and labels)
const tables = node.get('tables');
if (tables) {
tables.forEach((table) => {
if (table.get('rows')) {
table.get('rows').forEach(field => {
const keyPath = [nodeId, 'metadata', field.get('id')];
nodeMatches = findNodeMatch(nodeMatches, keyPath, field.get('value'),
query, prefix, field.get('label'));
});
}
});
}
} else if (metric) {
const metrics = node.get('metrics');
if (metrics) {
metrics.forEach(field => {
const keyPath = [nodeId, 'metrics', field.get('id')];
nodeMatches = findNodeMatchMetric(nodeMatches, keyPath, field.get('value'),
field.get('label'), metric, comp, value);
});
}
}
});
return nodeMatches;
@@ -71,17 +142,40 @@ export function parseQuery(query) {
if (query) {
const prefixQuery = query.split(PREFIX_DELIMITER);
const isPrefixQuery = prefixQuery && prefixQuery.length === 2;
const valid = !isPrefixQuery || prefixQuery.every(s => s);
if (valid) {
let prefix = null;
if (isPrefixQuery) {
prefix = prefixQuery[0];
query = prefixQuery[1];
if (isPrefixQuery) {
const prefix = prefixQuery[0].trim();
query = prefixQuery[1].trim();
if (prefix && query) {
return {
query,
prefix
};
}
return {
query,
prefix
};
} else if (COMPARISONS_REGEX.test(query)) {
// check for comparisons
let comparison;
COMPARISONS.forEach((comp, delim) => {
const comparisonQuery = query.split(delim);
if (comparisonQuery && comparisonQuery.length === 2) {
const value = parseValue(comparisonQuery[1]);
const metric = comparisonQuery[0].trim();
if (!isNaN(value) && metric) {
comparison = {
metric,
value,
comp
};
return false; // dont look further
}
}
return true;
});
if (comparison) {
return comparison;
}
} else {
return { query };
}
}
return null;

View File

@@ -67,5 +67,5 @@ export const formatDate = d3.time.format.iso;
const CLEAN_LABEL_REGEX = /\W/g;
export function slugify(label) {
return label.replace(CLEAN_LABEL_REGEX, '');
return label.replace(CLEAN_LABEL_REGEX, '').toLowerCase();
}