Skip to main content

Animal Metadata String Helper

const metadata = {
appliedFilterExp: '(animalType::Dog OR animalType::Cat) AND (animalColor="White" OR animalColor="Gray") AND (animalHabitat="Domestic") AND (animalAge:RANGE(>=2;<=8)) AND (animalVaccinated="true")',
datasetExp: 'animalcontent(breadth="full",depth="((animalCategory=\\"Mammal\\" AND animalType::OR(\\"Dog\\",\\"Cat\\",\\"Horse\\")) OR (animalCategory=\\"Reptile\\" AND animalType::OR(\\"Snake\\",\\"Lizard\\")))")',
searchQuery: 'playful'
};
function metadataToString(metadata) {
const appliedFilters = [];
// Match patterns like field::value, field="value", field:RANGE(...), field:FUNC(...)
const fieldRegex = /(\w+)(::|:|=)"?([^(AND|OR)"]+|\([^)]+\)|[A-Z]+\(.*?\))"?/g;
let match;

// 1. Extract all field-value pairs from appliedFilterExp
const exp = metadata.appliedFilterExp;
while ((match = fieldRegex.exec(exp)) !== null) {
const key = match[1];
const sep = match[2]; // seperator
const value = match[3].trim();
if (/^[A-Z]+\(.*\)$/.test(value)) {
// Handles any function-like value, e.g., RANGE(...), FUNC(...)
appliedFilters.push(`${key}:${value}`);
} else if (sep === '::') {
appliedFilters.push(`${key}::${value}`);
} else if (sep === '=' || sep === ':') {
appliedFilters.push(`${key}="${value}"`);
}
}

// 2. Extract animal categories from datasetExp
const catRegex = /animalCategory=\\?"([^"\\]+)\\?"/g;
const categories = new Set();
let catMatch;
// tslint:disable-next-line no-conditional-assignment
while ((catMatch = catRegex.exec(metadata.datasetExp)) !== null) {
categories.add(`animalCategory="${catMatch[1]}"`);
}
categories.forEach(cat => {
if (!appliedFilters.includes(cat)) {
appliedFilters.push(cat);
}
});

// 3. Build the metadata string
const appliedFiltersStr = appliedFilters.join('|#|');
const searchCategory = 'ALL';
const searchStrategy = metadata.searchQuery;
const searchStrategyJson = JSON.stringify({
strategyOutput: {
searchType: "",
queryItems: [{ fieldValue: metadata.searchQuery }]
}
});

return `appliedFilters=${appliedFiltersStr}@#@searchCategory=${searchCategory}@#@searchStrategy=${searchStrategy}@#@search_strategy_json=${searchStrategyJson}`;
}

1. Regex Definitions Before the While Loop

In the code, you may see multiple regex definitions before the while loop. For example:

  • fieldRegex

    const fieldRegex = /(\w+)(::|:|=)"?([^(AND|OR)"]+|\([^)]+\)|[A-Z]+\(.*?\))"?/g;

    This regex is used to extract key-value filter pairs from the filter expression string.

  • catRegex

    const catRegex = /animalCategory=\\?"([^"\\]+)\\?"/g;

    This regex is used to extract all animalCategory values from the dataset expression string.

    • It looks for animalCategory="SomeCategory" (with possible escaped quotes).
    • The capturing group ([^"\\]+) extracts the category name.

These regexes are defined before the while loops so they can be reused for multiple matches in the respective strings.


2. The While Loop and Regex Matching

let match;
while ((match = fieldRegex.exec(exp)) !== null) {
// process match
}
  • The while loop uses the regex’s .exec() method to repeatedly search the string for matches.
  • Each time a match is found, it returns an array (match) with details about the match.
  • When no more matches are found, .exec() returns null and the loop ends.

3. The Match Array

For each match, the array contains:

  • match[0]: The entire matched substring (e.g., animalType::Dog).
  • match[1]: The field/key name (e.g., animalType).
  • match[2]: The separator (e.g., ::).
  • match[3]: The value (e.g., Dog, White, RANGE(>=2;<=8)).

Example:
For animalColor="White":

  • match[0] = animalColor="White"
  • match[1] = animalColor
  • match[2] = =
  • match[3] = White

For animalAge:RANGE(>=2;<=8):

  • match[0] = animalAge:RANGE(>=2;<=8)
  • match[1] = animalAge
  • match[2] = :
  • match[3] = RANGE(>=2;<=8)

4. How They Work Together

  • Regexes are defined to match specific patterns in strings (filter expressions, categories, etc.).
  • The while loop uses .exec() to iterate through all matches in the string.
  • The match array provides the details of each match, allowing you to extract and process the key, separator, and value for each filter or category.

Summary:
Regexes are defined before the while loop to specify what patterns to look for. The while loop, using .exec(), finds all matches in the string. The match array (match[0], match[1], etc.) gives you the parts of each match, which you can use to build your final data structure or string. This approach is flexible and powerful for parsing structured data from text.

function parseMetadataString(metadataStr) {
const obj = {};
const parts = metadataStr.split('@#@');
parts.forEach(part => {
const [key, ...rest] = part.split('=');
const value = rest.join('=');
if (key === 'appliedFilters') {
obj.appliedFilters = value.split('|#|');
} else if (key === 'search_strategy_json') {
obj.search_strategy_json = JSON.parse(value);
} else {
obj[key] = value;
}
});
return obj;
}

// Example usage:
const metadata = 'appliedFilters=animalType::Dog|#|animalType::Cat|#|animalColor="White"|#|animalColor="Gray"|#|animalHabitat="Domestic"|#|animalAge:RANGE(>=2;<=8)|#|animalVaccinated="true"|#|animalCategory="Mammal"|#|animalCategory="Reptile"@#@searchCategory=ALL@#@searchStrategy=playful@#@search_strategy_json={"strategyOutput":{"searchType":"","queryItems":[{"fieldValue":"playful"}]}}';

Purpose

This function takes a metadata string (formatted with @#@ and |#| separators) and converts it back into a JavaScript object.


Step-by-Step Explanation

  1. Initialize an empty object

    const obj = {};
  2. Split the metadata string into parts

    const parts = metadataStr.split('@#@');
    • The string is split at each @#@, so each part represents a key-value pair (e.g., appliedFilters=..., searchCategory=...).
  3. Process each part

    parts.forEach(part => {
    const [key, ...rest] = part.split('=');
    const value = rest.join('=');
    // ...
    });
    • Each part is split at the first =.
    • key is the property name (e.g., appliedFilters).
    • value is the rest of the string after the =.
  4. Special handling for certain keys

    • If the key is appliedFilters, split the value by |#| to get an array.
    • If the key is search_strategy_json, parse the JSON string into an object.
    • Otherwise, just assign the value as a string.
    if (key === 'appliedFilters') {
    obj.appliedFilters = value.split('|#|');
    } else if (key === 'search_strategy_json') {
    obj.search_strategy_json = JSON.parse(value);
    } else {
    obj[key] = value;
    }
  5. Return the resulting object

    return obj;

Summary:
This function reverses the metadata string formatting, turning it back into a structured JavaScript object with arrays and nested objects where appropriate.

// Input
const appliedFilters = [
'animalType::Dog',
'animalType::Cat',
'animalColor="White"',
'animalColor="Gray"',
'animalHabitat="Domestic"',
'animalAge:RANGE(>=2;<=8)',
'animalVaccinated="true"',
'animalCategory="Mammal"',
'animalCategory="Reptile"'
];

// Convert to sub-category object
function appliedFiltersToObject(filters) {
const result = {};
filters.forEach(filter => {
let key, value;
if (filter.includes('::')) {
[key, value] = filter.split('::');
} else if (filter.includes(':RANGE(')) {
[key, value] = filter.split(':');
} else if (filter.includes('=')) {
[key, value] = filter.split('=');
value = value.replace(/^"|"$/g, ''); // remove quotes
}
if (!result[key]) result[key] = [];
result[key].push(value);
});
return result;
}

// Example usage:
console.log(appliedFiltersToObject(appliedFilters));

Purpose

The code takes an array of filter strings (like 'animalType::Dog') and converts it into an object where each key is a filter category (like animalType) and the value is an array of all values for that category (like ['Dog', 'Cat']).


How It Works

  • Each string in the input represents a filter in the format key::value, key="value", or key:RANGE(...).

2. The Function

  • Iterates over each filter string.
  • Splits the string into key and value based on the separator:
    • :: for things like animalType::Dog
    • :RANGE( for things like animalAge:RANGE(>=2;<=8)
    • = for things like animalColor="White"
  • Removes quotes from values if present.
  • Groups values by key in the result object, collecting all values for the same key into an array.

Summary

  • Input: Array of filter strings.
  • Output: Object with keys as filter categories and values as arrays of filter values.
  • Use case: Makes it easy to work with filters in a structured way, such as for building queries or displaying filter selections.