Spring Boot中MongoDB按条件筛选子文档数组的技术问题
Got it, let's break down how to fix your issue. The core problem right now is that your findByAbc_active query returns entire SwitchRepo documents including the full abc array, even though you only want the sub-documents where active=true. MongoDB doesn't automatically filter array elements when you do a top-level match—here's how to adjust your code to get the desired result:
Why Your Current Code Isn't Working
The findByAbc_active(boolean active) method matches any SwitchRepo document that contains at least one abc sub-document with active=true, but it returns the complete document (including all abc entries, even inactive ones). To filter the nested array, we need to use MongoDB's aggregation framework or projection with array filtering.
Solution 1: Use Aggregation with $filter (Clean & Efficient)
This approach uses MongoDB's $filter operator directly in a projection to keep only the active sub-documents in the abc array, without unwinding/rebuilding the array.
Step 1: Fix Your Entity Class
First, make your Abc inner class static—non-static inner classes require an instance of the outer class, which will cause mapping issues with MongoDB:
@Document(collection="switchrepo") public class SwitchRepo{ @Id private String id; private String type; private List<Abc> abc; // Getters, Setters, Constructors // Make this static for proper MongoDB mapping public static class Abc{ private String name; private String id; private String key; private boolean active; // Getters & Setters } }
Step 2: Add Aggregation Query to Your Repository
Extend your repository with a custom method using MongoTemplate (you can either add this directly to your repo or create a separate service):
@Repository public interface SwitchRepoDao extends MongoRepository<SwitchRepo, String> { // Default query (returns full documents) List<SwitchRepo> findByAbc_active(boolean active); // Custom method to filter active abc sub-documents default List<SwitchRepo> findActiveAbcSubDocuments(boolean active) { Aggregation aggregation = Aggregation.newAggregation( // Match documents that have at least one active abc entry Aggregation.match(Criteria.where("abc.active").is(active)), // Project only necessary fields, and filter the abc array Aggregation.project("id", "type") .and(ArrayOperators.Filter.filter("abc") .as("abcItem") .by(Criteria.where("abcItem.active").is(active))) .as("abc") ); return MongoTemplateUtils.getMongoTemplate().aggregate(aggregation, "switchrepo", SwitchRepo.class).getMappedResults(); } }
Note: If you don't have MongoTemplate injected, add @Autowired private MongoTemplate mongoTemplate; to your repository or service class.
Step 3: Update Your CommandLineRunner
Call the new custom method instead of findAllWithStatus:
@Bean CommandLineRunner runner(SwitchRepoDao switchRepoDao) { return args -> { List<SwitchRepo> configList = switchRepoDao.findActiveAbcSubDocuments(true); System.out.println("Configuration : "); for (SwitchRepo config : configList) { System.out.println("ID: " + config.getId() + ", Active ABC Entries: " + config.getAbc()); } }; }
Solution 2: Aggregation with unwind + match + group
If you prefer a more explicit pipeline (useful for more complex logic), you can unwind the array, filter the sub-documents, then rebuild the array:
default List<SwitchRepo> findActiveAbcSubDocuments(boolean active) { Aggregation aggregation = Aggregation.newAggregation( Aggregation.match(Criteria.where("abc.active").is(active)), // Unwind the abc array to treat each sub-document as a separate document Aggregation.unwind("abc"), // Filter to keep only active sub-documents Aggregation.match(Criteria.where("abc.active").is(active)), // Rebuild the abc array for each original document Aggregation.group("_id", "type").push("abc").as("abc") ); return mongoTemplate.aggregate(aggregation, "switchrepo", SwitchRepo.class).getMappedResults(); }
Expected Output
For your sample MongoDB document, both solutions will return:
Configuration : ID: 1234567890, Active ABC Entries: [Abc{name='test', id='test1', key='secret', active=true}]
内容的提问来源于stack exchange,提问作者user11546578

