如何降低基于DTO的多字段商品搜索方法的代码复杂度?
Optimized DTO-Based Product Search Implementation for MongoDB
Looking at your code, the main pain points are repetitive null checks for the Criteria object and duplicated logic across each search field. Let's refactor this to make it cleaner, more maintainable, and less error-prone.
Key Issues in the Original Code:
- Redundant null checks (
criteria != null ? ... : ...) cluttering every field's condition - Duplicate regex/exact-match logic repeated across multiple fields
- Unnecessary re-assignment of
Pageablewhen building the query - Potential bug: Primitive numeric types (like
intfor price/stock) can't distinguish between a user searching for 0 vs. the default "not set" state
Optimized Solution:
We'll extract repetitive criteria-building logic into helper methods, eliminate redundant checks, and streamline query construction:
import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.apache.commons.lang3.StringUtils; import java.util.List; import java.util.stream.Collectors; public class ProductService { private static final String CASE_INSENSITIVE_FLAG = "i"; private final ProductMapper productMapper; private final MongoTemplate mongoTemplate; // Constructor with dependency injection public ProductService(ProductMapper productMapper, MongoTemplate mongoTemplate) { this.productMapper = productMapper; this.mongoTemplate = mongoTemplate; } public List<ProductDTO> search(SearchProductDTO productDTO) { Criteria criteria = null; // Add case-insensitive regex criteria for string fields criteria = appendRegexCriteria(criteria, NAME.getLabel(), productDTO.getName()); criteria = appendRegexCriteria(criteria, DESCRIPTION.getLabel(), productDTO.getDescription()); criteria = appendRegexCriteria(criteria, BRAND.getLabel(), productDTO.getBrand()); criteria = appendRegexCriteria(criteria, TYPE.getLabel(), productDTO.getType()); criteria = appendRegexCriteria(criteria, COLOR.getLabel(), productDTO.getColor()); // Add exact-match criteria for non-string fields criteria = appendExactMatchCriteria(criteria, PRICE.getLabel(), productDTO.getPrice(), 0); criteria = appendExactMatchCriteria(criteria, STOCK_COUNT.getLabel(), productDTO.getStockCount(), 0); criteria = appendExactMatchCriteria(criteria, SIZE.getLabel(), productDTO.getProductSize(), null); criteria = appendExactMatchCriteria(criteria, GENDER.getLabel(), productDTO.getGender(), null); // Build query with pagination Pageable pageable = PageRequest.of(productDTO.getPage(), productDTO.getSize()); Query query = new Query(pageable); if (criteria != null) { query.addCriteria(criteria); } // Fetch and map results to DTOs List<Product> products = mongoTemplate.find(query, Product.class); return products.stream() .map(productMapper::modelToDto) .collect(Collectors.toList()); } /** * Helper to append case-insensitive regex criteria if the value is not blank */ private Criteria appendRegexCriteria(Criteria currentCriteria, String fieldName, String value) { if (StringUtils.isNotBlank(value)) { return currentCriteria == null ? Criteria.where(fieldName).regex(value, CASE_INSENSITIVE_FLAG) : currentCriteria.and(fieldName).regex(value, CASE_INSENSITIVE_FLAG); } return currentCriteria; } /** * Helper to append exact-match criteria if the value is not the default * Works for both primitive types (using default value) and reference types (checking for null) */ private <T> Criteria appendExactMatchCriteria(Criteria currentCriteria, String fieldName, T value, T defaultValue) { if (value != null && !value.equals(defaultValue)) { return currentCriteria == null ? Criteria.where(fieldName).is(value) : currentCriteria.and(fieldName).is(value); } return currentCriteria; } }
Improvements Explained:
- Helper Methods: Encapsulates repetitive null checks and condition logic, making the main search method concise and easy to scan.
- Readability: Each search field's criteria is added in a single line, making it simple to modify or add new fields later.
- Reduced Redundancy: Eliminates duplicated ternary operators and unnecessary
Pageablere-assignments. - Maintainability: Adding a new search field only requires one line in the main method, no need to rewrite null-check logic.
Further Recommendations:
- Use Wrapper Types for Numerics: Change
priceandstockCountinSearchProductDTOtoInteger/Doubleinstead of primitives. This lets you usenullto indicate "not set" instead of relying on 0, fixing the bug where users can't search for items with price=0 or stock=0. - Add DTO Validation: Validate pagination parameters (e.g., page number ≥0, size ≤50) to prevent invalid requests.
- Consider Querydsl: If you're open to adding a dependency, Spring Data MongoDB's Querydsl integration enables type-safe query building with even less boilerplate.
内容的提问来源于stack exchange,提问作者ElbrusGarayev
相关产品推荐
相关产品推荐

