logo️ ObjectQuel
Getting Started
  • Introduction
  • Installation
  • Quick Start
Core Features
  • Query Language
  • Data Mapper Architecture
  • Entity Management & Annotations
  • Relationship Mapping
  • Searching
  • Entity Bridge
  • Cascading
  • Indexing System
  • Schema Management (Sculpt CLI)
Advanced Topics
  • Lifecycle callbacks
  • Repositories
  • Hybrid data sources (JSON + database)
  • Optimistic Locking
API Reference
  • Annotations Reference
  • Core Classes
Contributing
  • How to Contribute
  • License

Searching

ObjectQuel provides three approaches for matching and searching text: wildcard patterns, regular expressions, and full-text search. All three are used within where clauses and can be combined with other filter conditions. This page covers each approach from simplest to most powerful.

Wildcard Patterns

Wildcard patterns are the simplest way to match text in ObjectQuel. Place the pattern in single quotes and use the = operator — ObjectQuel detects the wildcard characters and converts the comparison into a pattern match automatically.

Two wildcard characters are available:

  • * — matches any number of characters (including zero)
  • ? — matches exactly one character

Prefix Matching

The most efficient wildcard pattern. When the pattern starts with a literal string, ObjectQuel can use an index to narrow results before scanning:

// Find all products whose name starts with "iPhone"
$results = $entityManager->executeQuery("
    range of p is App\\Entity\\ProductEntity
    retrieve (p) where p.name = 'iPhone*'
");

Suffix Matching

Matches the end of a string. Less efficient than prefix matching because the database cannot use a standard index for trailing matches:

// Find all products whose name ends with "Pro"
$results = $entityManager->executeQuery("
    range of p is App\\Entity\\ProductEntity
    retrieve (p) where p.name = '*Pro'
");

Contains Matching

Matches anywhere within a string. This always requires a full table scan — use sparingly on large tables:

// Find all products with "wireless" anywhere in the name
$results = $entityManager->executeQuery("
    range of p is App\\Entity\\ProductEntity
    retrieve (p) where p.name = '*wireless*'
");

Single-Character Matching

The ? wildcard matches exactly one character. Use it when you know the structure of the string but not the exact characters:

// Match product codes like "A1B", "X9Z", etc.
$results = $entityManager->executeQuery("
    range of p is App\\Entity\\ProductEntity
    retrieve (p) where p.code = '???'
");

Combining Wildcards

Mix * and ? in a single pattern for more precise matching:

// Match SKUs like "ABC-anything-3chars-anything-XYZ"
// e.g., "ABC-PRO-001-BLUE-XYZ" or "ABC--X7Q-TEST-XYZ"
$results = $entityManager->executeQuery("
    range of p is App\\Entity\\ProductEntity
    retrieve (p) where p.sku = 'ABC*-???-*XYZ'
");

Negating Wildcard Patterns

Use != to find rows that do not match a pattern:

// Find all products whose name does NOT start with "Test"
$results = $entityManager->executeQuery("
    range of p is App\\Entity\\ProductEntity
    retrieve (p) where p.name != 'Test*'
");

Wildcard Performance

Pattern Example Index usage
Prefix 'iPhone*' Can use index — fast
Suffix '*Pro' Full scan
Contains '*wireless*' Full scan
Single-char '???' Full scan
Mixed 'ABC*-???-*XYZ' Prefix portion may use index

Regular Expressions

When wildcards aren't expressive enough, ObjectQuel supports regular expressions. Enclose the pattern in forward slashes instead of quotes:

// Match company email addresses
$results = $entityManager->executeQuery("
    range of u is App\\Entity\\UserEntity
    retrieve (u) where u.email = /^[a-zA-Z0-9._%+-]+@company\.com$/
");

ObjectQuel uses the regex syntax supported by your underlying database engine.

Regex Flags

Append flags after the closing slash to modify matching behavior:

Flag Meaning Example
i Case-insensitive matching /smartphone/i
// Case-insensitive: matches "Smartphone", "SMARTPHONE", "smartphone", etc.
$results = $entityManager->executeQuery("
    range of p is App\\Entity\\ProductEntity
    retrieve (p) where p.description = /smartphone/i
");

Common Regex Patterns

// Starts with a digit
$results = $entityManager->executeQuery("
    range of p is App\\Entity\\ProductEntity
    retrieve (p) where p.sku = /^[0-9]/
");

// Contains exactly 3 consecutive uppercase letters
$results = $entityManager->executeQuery("
    range of p is App\\Entity\\ProductEntity
    retrieve (p) where p.code = /[A-Z]{3}/
");

// Matches a phone number format: +31-6-12345678
$results = $entityManager->executeQuery("
    range of u is App\\Entity\\UserEntity
    retrieve (u) where u.phone = /^\+[0-9]{1,3}-[0-9]+-[0-9]+$/
");
Performance note: Regular expressions always require a full table scan. On large tables, consider whether a wildcard pattern or full-text search could achieve the same result more efficiently.

Full-Text Search

For natural language queries — where you want to find rows containing certain words regardless of their position — use the search() function. This leverages database full-text indexing for fast, relevance-ranked results.

Single-Field Search

// Find products with "bluetooth" in the description
$results = $entityManager->executeQuery("
    range of p is App\\Entity\\ProductEntity
    retrieve (p) where search(p.description, 'bluetooth speaker')
");

Multi-Field Search

Pass multiple fields to search across several columns at once:

// Search both name and description
$results = $entityManager->executeQuery("
    range of p is App\\Entity\\ProductEntity
    retrieve (p) where search(p.name, p.description, 'premium quality')
");

Boolean Operators

Control which terms are required, excluded, or optional using boolean prefixes in the search string:

Operator Meaning Example
+word Must be present in results +bluetooth
-word Must not be present in results -expensive
word Optional — boosts ranking if present wireless
// Must include bluetooth, must not include expensive,
// wireless is optional but boosts ranking
$results = $entityManager->executeQuery("
    range of p is App\\Entity\\ProductEntity
    retrieve (p) where search(p.description, 'wireless +bluetooth -expensive')
");

Combining Search with Other Filters

The search() function is a regular where-clause condition and can be combined with any other filter:

// Full-text search combined with price range and category filter
$results = $entityManager->executeQuery("
    range of p is App\\Entity\\ProductEntity
    range of c is App\\Entity\\CategoryEntity via p.category
    retrieve (p, c.name)
    where search(p.name, p.description, '+wireless +bluetooth')
      and p.price between :min and :max
      and c.name = 'Electronics'
    sort by p.price asc
", [
    'min' => 25.00,
    'max' => 200.00
]);

Searching Across Related Entities

You can apply search() to fields on related entities, just as you would any other where-clause condition:

// Find orders where the product description mentions "organic"
$results = $entityManager->executeQuery("
    range of o is App\\Entity\\OrderEntity
    range of i is App\\Entity\\OrderItemEntity via o.items
    range of p is App\\Entity\\ProductEntity via i.product
    retrieve (o, p.name)
    where search(p.description, '+organic')
      and o.status = 'completed'
");
Full-text indexes are required. The search() function relies on your database's full-text indexing. Make sure the columns you search on have a full-text index configured, otherwise the query will fail or fall back to a slow scan.

Quick Reference

Approach Best for Performance
Wildcards Prefix, suffix, and contains matching Fast for prefix patterns, slower for contains
Regular Expressions Complex validation and structural patterns Slower — always requires a full scan
Full-Text Search Natural language queries with relevance ranking Fast — uses dedicated full-text indexes
logo️ ObjectQuel

Declarative query engine for PHP developers who care about clarity, control, and maintainable data access.

Resources
  • GitHub
  • Packagist
  • Documentation
  • Releases
Community
  • Contributing Guide
  • Issue Tracker
Learn
  • Getting Started
  • Query Language
  • Architecture

© 2025-2026 Quellabs. Released under the MIT License.

Questions about ObjectQuel? floris@quellabs.com

Built with ❤️ for the PHP community