Skip to content

Dynamic Queries

One of TorpedoQuery's strongest features is the ability to build queries dynamically at runtime. This is especially useful for creating search screens, implementing flexible filtering, and adapting queries based on user input.

Conditional Clause Addition

The most basic form of dynamic query building is conditionally adding WHERE clauses:

Entity entity = from(Entity.class);

// Only add this condition if the parameter is provided
String name = getUserInput();
if (name != null && !name.isEmpty()) {
    where(entity.getName()).eq(name);
}

// Add another condition if needed
Integer minValue = getMinValueInput();
if (minValue != null) {
    where(entity.getIntegerField()).gte(minValue);
}

Query<Entity> query = select(entity);

This results in a query that dynamically includes only the conditions for which values were provided.

Using Empty Conditions

TorpedoQuery allows you to create empty condition placeholders that can be filled later:

Entity entity = from(Entity.class);
OnGoingLogicalCondition emptyCondition = condition();
Query<Entity> query = select(entity);

// Later in your code, you can add to this condition
if (shouldFilterByName()) {
    emptyCondition.and(entity.getName()).eq("test");
}

if (shouldFilterByCode()) {
    emptyCondition.and(entity.getCode()).like().startsWith("ABC");
}

// Finally, apply the condition if it's not empty
if (!emptyCondition.isEmpty()) {
    where(emptyCondition);
}

If no conditions are added to the empty condition, no WHERE clause will be added to the query.

Testing If Conditions Have Been Added

You can check if conditions have already been added to your query:

Entity entity = from(Entity.class);
Query<Entity> query = select(entity);

// Check if conditions exist
Optional<OnGoingLogicalCondition> existingCondition = query.condition();
if (existingCondition.isPresent()) {
    // Add to existing condition
    existingCondition.get().and(entity.getCode()).eq("newCode");
} else {
    // Create new condition
    where(entity.getCode()).eq("newCode");
}

Building Complex Dynamic Conditions

For more complex scenarios, you can build condition groups:

Entity entity = from(Entity.class);

// Create a condition group for status filters
OnGoingLogicalCondition statusConditions = condition();
if (includeActive()) {
    statusConditions.or(entity.isActive()).eq(true);
}
if (includePending()) {
    statusConditions.or(entity.getStatus()).eq("PENDING");
}

// Create a condition group for date filters
OnGoingLogicalCondition dateConditions = condition();
if (startDate != null) {
    dateConditions.and(entity.getDateField()).gte(startDate);
}
if (endDate != null) {
    dateConditions.and(entity.getDateField()).lte(endDate);
}

// Combine the condition groups
if (!statusConditions.isEmpty() && !dateConditions.isEmpty()) {
    where(statusConditions).and(dateConditions);
} else if (!statusConditions.isEmpty()) {
    where(statusConditions);
} else if (!dateConditions.isEmpty()) {
    where(dateConditions);
}

Query<Entity> query = select(entity);

Dynamic Joins

You can also dynamically add joins based on requirements:

Entity entity = from(Entity.class);

// Only join with sub-entities if we need them
if (needSubEntityData()) {
    SubEntity subEntity = innerJoin(entity.getSubEntities());

    // Add conditions on the joined entity
    if (subEntityCode != null) {
        with(subEntity.getCode()).eq(subEntityCode);
    }
}

Query<Entity> query = select(entity);

Dynamic Select Clauses

Create dynamic SELECT clauses to determine what fields to return:

Entity entity = from(Entity.class);
List<Object> selectFields = new ArrayList<>();

// Always select the ID
selectFields.add(entity.getId());

// Conditionally add other fields
if (includeBasicInfo()) {
    selectFields.add(entity.getName());
    selectFields.add(entity.getCode());
}

if (includeMetrics()) {
    selectFields.add(entity.getIntegerField());
    selectFields.add(entity.getBigDecimalField());
}

// Convert to array
Object[] selectArray = selectFields.toArray();

// Create query with dynamic select fields
Query<Object[]> query = select(selectArray);

Dynamic Sorting

Add sorting criteria dynamically:

Entity entity = from(Entity.class);

// Get sort field and direction from user
String sortField = getSortField(); // e.g. "name", "code", "date"
String sortDirection = getSortDirection(); // "asc" or "desc"

// Apply sort based on user input
if ("name".equals(sortField)) {
    if ("desc".equals(sortDirection)) {
        orderBy(desc(entity.getName()));
    } else {
        orderBy(asc(entity.getName()));
    }
} else if ("code".equals(sortField)) {
    if ("desc".equals(sortDirection)) {
        orderBy(desc(entity.getCode()));
    } else {
        orderBy(asc(entity.getCode()));
    }
} else if ("date".equals(sortField)) {
    if ("desc".equals(sortDirection)) {
        orderBy(desc(entity.getDateField()));
    } else {
        orderBy(asc(entity.getDateField()));
    }
}

Query<Entity> query = select(entity);

Search Form Example

Here's a comprehensive example of implementing a search form:

public List<Entity> search(SearchForm form) {
    Entity entity = from(Entity.class);

    // Start with an empty condition
    OnGoingLogicalCondition searchCondition = condition();

    // Add text search if provided
    if (form.getSearchText() != null && !form.getSearchText().isEmpty()) {
        String searchPattern = "%" + form.getSearchText() + "%";
        OnGoingLogicalCondition textSearch = condition(entity.getName()).like(searchPattern)
            .or(entity.getCode()).like(searchPattern);
        searchCondition.and(textSearch);
    }

    // Add date range if provided
    if (form.getStartDate() != null) {
        searchCondition.and(entity.getDateField()).gte(form.getStartDate());
    }
    if (form.getEndDate() != null) {
        searchCondition.and(entity.getDateField()).lte(form.getEndDate());
    }

    // Add status filter if provided
    if (form.getStatusList() != null && !form.getStatusList().isEmpty()) {
        searchCondition.and(entity.getStatus()).in(form.getStatusList());
    }

    // Apply the condition if not empty
    if (!searchCondition.isEmpty()) {
        where(searchCondition);
    }

    // Add sorting
    if ("dateDesc".equals(form.getSortOption())) {
        orderBy(desc(entity.getDateField()));
    } else if ("dateAsc".equals(form.getSortOption())) {
        orderBy(asc(entity.getDateField()));
    } else if ("nameAsc".equals(form.getSortOption())) {
        orderBy(asc(entity.getName()));
    } else {
        // Default sorting
        orderBy(desc(entity.getDateField()));
    }

    // Create and execute the query
    Query<Entity> query = select(entity);
    return query.list(entityManager);
}

Advanced Example: Dynamic Report Builder

Here's an advanced example that builds a complex report query dynamically:

public List<ReportData> generateReport(ReportSettings settings) {
    Entity entity = from(Entity.class);

    // Dynamic joins based on included data
    SubEntity subEntity = null;
    if (settings.includeSubEntityData()) {
        subEntity = leftJoin(entity.getSubEntities());
    }

    // Build select fields based on requested columns
    List<Object> selectFields = new ArrayList<>();
    for (String column : settings.getColumns()) {
        switch (column) {
            case "id":
                selectFields.add(entity.getId());
                break;
            case "name":
                selectFields.add(entity.getName());
                break;
            case "active":
                selectFields.add(entity.isActive());
                break;
            case "subEntityCount":
                if (subEntity != null) {
                    selectFields.add(count(subEntity));
                }
                break;
            case "avgValue":
                if (subEntity != null) {
                    selectFields.add(avg(subEntity.getNumberField()));
                }
                break;
            // Add other columns as needed
        }
    }

    // Add conditions based on filters
    OnGoingLogicalCondition filterCondition = condition();
    for (ReportFilter filter : settings.getFilters()) {
        switch (filter.getField()) {
            case "name":
                addStringFilter(filterCondition, entity.getName(), filter);
                break;
            case "active":
                addBooleanFilter(filterCondition, entity.isActive(), filter);
                break;
            case "date":
                addDateFilter(filterCondition, entity.getDateField(), filter);
                break;
            case "value":
                addNumericFilter(filterCondition, entity.getIntegerField(), filter);
                break;
            // Add other filters as needed
        }
    }

    if (!filterCondition.isEmpty()) {
        where(filterCondition);
    }

    // Add grouping if needed for aggregations
    if (settings.isAggregated()) {
        groupBy(entity.getId(), entity.getName(), entity.isActive());
    }

    // Add sorting
    for (ReportSorting sort : settings.getSorting()) {
        switch (sort.getField()) {
            case "name":
                addSorting(entity.getName(), sort.getDirection());
                break;
            case "date":
                addSorting(entity.getDateField(), sort.getDirection());
                break;
            // Add other sort fields as needed
        }
    }

    // Create and execute query
    Query<Object[]> query = select(selectFields.toArray());
    List<Object[]> results = query.list(entityManager);

    // Convert results to report data objects
    return convertToReportData(results, settings.getColumns());
}

// Helper methods for the report builder
private void addStringFilter(OnGoingLogicalCondition condition, String field, ReportFilter filter) {
    // Implementation details...
}

private void addSorting(Object field, String direction) {
    if ("desc".equals(direction)) {
        orderBy(desc(field));
    } else {
        orderBy(asc(field));
    }
}

Dynamic query building with TorpedoQuery enables you to create powerful, flexible queries that adapt to user input while maintaining type safety and clean code structure.