Animal Metadata String Helper
- Input
- Output
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'
};
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"}]}}';
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
animalCategoryvalues from the dataset expression string.- It looks for
animalCategory="SomeCategory"(with possible escaped quotes). - The capturing group
([^"\\]+)extracts the category name.
- It looks for
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()returnsnulland 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]=animalColormatch[2]==match[3]=White
For animalAge:RANGE(>=2;<=8):
match[0]=animalAge:RANGE(>=2;<=8)match[1]=animalAgematch[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
- Output
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"}]}}';
{
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
-
Initialize an empty object
const obj = {}; -
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=...).
- The string is split at each
-
Process each part
parts.forEach(part => {
const [key, ...rest] = part.split('=');
const value = rest.join('=');
// ...
});- Each part is split at the first
=. keyis the property name (e.g.,appliedFilters).valueis the rest of the string after the=.
- Each part is split at the first
-
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;
} - If the key is
-
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.
- Function
- Output
// 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));
{
animalType: ['Dog', 'Cat'],
animalColor: ['White', 'Gray'],
animalHabitat: ['Domestic'],
animalAge: ['RANGE(>=2;<=8)'],
animalVaccinated: ['true'],
animalCategory: ['Mammal', 'Reptile']
}
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", orkey:RANGE(...).
2. The Function
- Iterates over each filter string.
- Splits the string into
keyandvaluebased on the separator:::for things likeanimalType::Dog:RANGE(for things likeanimalAge:RANGE(>=2;<=8)=for things likeanimalColor="White"
- Removes quotes from values if present.
- Groups values by key in the
resultobject, 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.