Overview
String operators enable powerful text matching and validation capabilities. Rule Engine JS provides four string operators for different text comparison needs:
contains Check if string contains substring
startsWith Check if string starts with prefix
endsWith Check if string ends with suffix
regex Match against regular expression patterns
Architecture
String operators are implemented across two classes extending BaseOperator:
Source Files:
String operators: src/operators/string.js
Regex operator: src/operators/regex.js
Base operator: src/operators/base/BaseOperator.js
Type utilities: src/utils/TypeUtils.js
Unit tests: tests/unit/operators/string.test.js
Key Features
Dynamic field comparison - Compare two fields or field to literal value
Case sensitive by default - Exact matching for security
Automatic type coercion - Non-string values converted to strings
Pattern caching - Regex patterns cached for performance
Comprehensive validation - Validates argument count and types
contains - Substring Match
Check if a string contains a substring.
Syntax
{ contains : [ haystack , needle ] }
{ contains : [ haystack , needle , options ] }
Parameters
The string to search in - field path or literal value
The substring to search for - field path or literal value
Configuration options Enable strict type checking - prevents automatic string coercion
Returns
boolean - true if haystack contains needle, false otherwise
Examples
Basic Contains
Dynamic Field Comparison
Email Domain Validation
With Rule Helpers
import { createRuleEngine } from 'rule-engine-js' ;
const engine = createRuleEngine ();
const data = {
user: {
email: 'john@company.com' ,
bio: 'Software Engineer at TechCorp' ,
}
};
// Check if email contains domain
engine . evaluateExpr ({ contains: [ 'user.email' , '@company.com' ] }, data );
// Result: { success: true }
// Check bio for keyword
engine . evaluateExpr ({ contains: [ 'user.bio' , 'Software' ] }, data );
// Result: { success: true }
// Case sensitive - will fail
engine . evaluateExpr ({ contains: [ 'user.bio' , 'software' ] }, data );
// Result: { success: false }
Common Use Cases
const article = {
title: 'Introduction to React Hooks' ,
content: 'React Hooks provide a way to use state...' ,
tags: [ 'react' , 'hooks' , 'javascript' ]
};
// Search in title or content
const searchRule = {
or: [
{ contains: [ 'title' , 'React' ] },
{ contains: [ 'content' , 'React' ] }
]
};
engine . evaluateExpr ( searchRule , article );
// Result: { success: true }
const post = {
content: 'Check out this amazing product!' ,
author: 'spammer@example.com'
};
// Filter spam content
const spamRule = {
or: [
{ contains: [ 'content' , 'amazing product' ] },
{ contains: [ 'content' , 'click here' ] },
{ contains: [ 'author' , 'spammer' ] }
]
};
engine . evaluateExpr ( spamRule , post );
// Result: { success: true } - Flagged as spam
const product = {
name: 'MacBook Pro 16-inch' ,
category: 'Electronics > Computers > Laptops'
};
// Check product category
const isLaptop = { contains: [ 'category' , 'Laptops' ] };
const isElectronics = { contains: [ 'category' , 'Electronics' ] };
engine . evaluateExpr ( isLaptop , product );
// Result: { success: true }
Case Sensitivity : contains is case-sensitive by default. “Software” !== “software”
startsWith - Prefix Match
Check if a string starts with a specific prefix.
Syntax
{ startsWith : [ string , prefix ] }
{ startsWith : [ string , prefix , options ] }
Parameters
The string to check - field path or literal value
The prefix to match - field path or literal value
Configuration options Enable strict type checking
Returns
boolean - true if string starts with prefix, false otherwise
Examples
Basic StartsWith
Dynamic Prefix
ID Prefix Validation
const data = {
user: {
name: 'John Doe' ,
username: 'john_doe_123'
},
url: 'https://example.com/page'
};
// Check name prefix
engine . evaluateExpr ({ startsWith: [ 'user.name' , 'John' ] }, data );
// Result: { success: true }
// Check URL protocol
engine . evaluateExpr ({ startsWith: [ 'url' , 'https' ] }, data );
// Result: { success: true }
// Check username pattern
engine . evaluateExpr ({ startsWith: [ 'user.username' , 'john_' ] }, data );
// Result: { success: true }
Common Use Cases
const links = [
{ url: 'https://secure.example.com' },
{ url: 'http://example.com' },
{ url: 'ftp://files.example.com' }
];
// Only allow HTTPS
const httpsOnly = { startsWith: [ 'url' , 'https://' ] };
links . forEach ( link => {
const result = engine . evaluateExpr ( httpsOnly , link );
console . log ( ` ${ link . url } : ${ result . success } ` );
});
const file = {
path: '/app/uploads/images/photo.jpg' ,
allowedPrefix: '/app/uploads/'
};
// Ensure file is in allowed directory
const inAllowedDir = {
startsWith: [ 'path' , 'allowedPrefix' ]
};
engine . evaluateExpr ( inAllowedDir , file );
// Result: { success: true }
endsWith - Suffix Match
Check if a string ends with a specific suffix.
Syntax
{ endsWith : [ string , suffix ] }
{ endsWith : [ string , suffix , options ] }
Parameters
The string to check - field path or literal value
The suffix to match - field path or literal value
Configuration options Enable strict type checking
Returns
boolean - true if string ends with suffix, false otherwise
Examples
Basic EndsWith
Dynamic Suffix
File Type Validation
const data = {
user: {
email: 'john@company.com' ,
},
file: {
name: 'document.pdf' ,
backup: 'document.pdf.bak'
}
};
// Check email domain
engine . evaluateExpr ({ endsWith: [ 'user.email' , '.com' ] }, data );
// Result: { success: true }
// Check file extension
engine . evaluateExpr ({ endsWith: [ 'file.name' , '.pdf' ] }, data );
// Result: { success: true }
// Check backup file
engine . evaluateExpr ({ endsWith: [ 'file.backup' , '.bak' ] }, data );
// Result: { success: true }
Common Use Cases
const upload = {
filename: 'profile-photo.jpg' ,
allowedTypes: [ '.jpg' , '.jpeg' , '.png' , '.gif' ]
};
// Accept only image files
const imageRule = {
or: [
{ endsWith: [ 'filename' , '.jpg' ] },
{ endsWith: [ 'filename' , '.jpeg' ] },
{ endsWith: [ 'filename' , '.png' ] },
{ endsWith: [ 'filename' , '.gif' ] }
]
};
engine . evaluateExpr ( imageRule , upload );
// Result: { success: true }
const user = {
email: 'employee@company.com' ,
isInternal: true
};
// Only allow company email
const companyEmailRule = {
and: [
{ endsWith: [ 'email' , '@company.com' ] },
{ eq: [ 'isInternal' , true ] }
]
};
engine . evaluateExpr ( companyEmailRule , user );
// Result: { success: true }
const request = {
path: '/api/v1/users/profile.json' ,
requiresJson: true
};
// API must return JSON
const jsonApiRule = {
and: [
{ startsWith: [ 'path' , '/api/' ] },
{ endsWith: [ 'path' , '.json' ] }
]
};
engine . evaluateExpr ( jsonApiRule , request );
// Result: { success: true }
regex - Regular Expression Match
Match strings against regular expression patterns with support for flags and pattern caching.
Syntax
{ regex : [ text , pattern ] }
{ regex : [ text , pattern , options ] }
Parameters
The text to match against - field path or literal value
The regular expression pattern - field path or literal value
Configuration options Regex flags: i (case-insensitive), g (global), m (multiline), etc.
Enable strict type checking
Returns
boolean - true if text matches pattern, false otherwise
Examples
Email Validation
Phone Number Validation
Case-Insensitive Search
Dynamic Patterns
Password Strength
const user = {
email: 'john.doe@company.com'
};
// Validate email format
const emailRule = {
regex: [ 'email' , '^[ \\ w \\ .-]+@[ \\ w \\ .-]+ \\ .[a-zA-Z]{2,}$' ]
};
engine . evaluateExpr ( emailRule , user );
// Result: { success: true }
// Test with invalid email
const invalidUser = { email: 'invalid.email' };
engine . evaluateExpr ( emailRule , invalidUser );
// Result: { success: false }
Common Use Cases
const link = {
url: 'https://www.example.com/path?query=value'
};
// Validate URL format
const urlRule = {
regex: [ 'url' , '^https?: \\ / \\ /[ \\ w \\ .-]+(:[ \\ d]+)?( \\ /[ \\ w \\ .-]*)* \\ ??([ \\ w=&]*)$' ]
};
engine . evaluateExpr ( urlRule , link );
// Result: { success: true }
const payment = {
cardNumber: '4532-1234-5678-9010'
};
// Validate credit card format (Visa)
const visaRule = {
regex: [ 'cardNumber' , '^4[0-9]{3}-?[0-9]{4}-?[0-9]{4}-?[0-9]{4}$' ]
};
engine . evaluateExpr ( visaRule , payment );
// Result: { success: true }
const user = {
username: 'john_doe_123'
};
// Username: 3-20 chars, alphanumeric + underscore
const usernameRule = {
regex: [ 'username' , '^[a-zA-Z0-9_]{3,20}$' ]
};
engine . evaluateExpr ( usernameRule , user );
// Result: { success: true }
Performance : Regex patterns are cached automatically for better performance when evaluating the same pattern multiple times.
Error Handling
Common Errors
const data = { user: { age: 28 } };
// Trying to use contains on number
const result = engine . evaluateExpr (
{ contains: [ 'user.age' , '28' ] },
data
);
// Returns:
// {
// success: false,
// error: "CONTAINS operator requires string operands"
// }
const data = { text: 'hello' };
// Invalid regex syntax
const result = engine . evaluateExpr (
{ regex: [ 'text' , '[invalid(' ] },
data
);
// Returns:
// {
// success: false,
// error: "Invalid regex pattern: [invalid("
// }
// Missing second argument
const result = engine . evaluateExpr (
{ contains: [ 'user.email' ] },
data
);
// Returns:
// {
// success: false,
// error: "CONTAINS operator requires 2-3 arguments, got 1"
// }
const data = { user: { name: 'John' } };
// Field doesn't exist - undefined coerced to empty string
const result = engine . evaluateExpr (
{ contains: [ 'user.email' , '@' ] },
data
);
// Returns:
// {
// success: false,
// error: "CONTAINS operator requires string operands"
// }
Error Recovery
function safeStringMatch ( engine , operator , field , pattern , data , fallback = false ) {
const result = engine . evaluateExpr ({ [operator]: [ field , pattern ] }, data );
if ( ! result . success ) {
console . error ( `String match failed: ${ result . error } ` );
return fallback ;
}
return result . success ;
}
// Usage
const hasKeyword = safeStringMatch (
engine ,
'contains' ,
'content' ,
'important' ,
article ,
false
);
Type Coercion
String operators support automatic type coercion in loose mode:
Input Type Coercion Behavior Example
stringNo change "hello" → "hello"numberConverted to string 123 → "123"booleanConverted to string true → "true"nullError thrown null → ErrorundefinedError thrown undefined → ErrorobjectError thrown {} → Error
Strict Mode
const data = { value: 123 };
// Loose mode (default) - coerces number to string
engine . evaluateExpr ({ contains: [ 'value' , '12' ] }, data );
// Result: { success: true } - "123" contains "12"
// Strict mode - rejects non-string
const strictEngine = createRuleEngine ({ strict: true });
strictEngine . evaluateExpr ({ contains: [ 'value' , '12' ] }, data );
// Result: { success: false, error: "requires string operands" }
API Reference
For complete API documentation: