spring-data-mongodb

v2026.09.24

Spring Data MongoDB for Java/Spring Boot applications. Covers repositories, MongoTemplate, aggregations, and document mapping. USE WHEN: user mentions "spring data mongodb", "MongoTemplate", "MongoRepository", "@Document", "Spring Boot MongoDB", "aggregation pipeline Java" DO NOT USE FOR: raw MongoDB driver - use `mongodb` instead, relational databases - use `spring-data-jpa` or `spring-data-jdbc` instead

GitHub
Install command
npx skhub add claude-dev-suite/spring-data-mongodb
Markdown
SKILL.md

Spring Data MongoDB - Quick Reference

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: spring-data-mongodb for comprehensive documentation.

Setup

Dependencies (Maven)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

Configuration

spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/mydb
      # Or explicit
      host: localhost
      port: 27017
      database: mydb
      username: user
      password: secret
      authentication-database: admin

Entity Mapping

Basic Document

@Document(collection = "products")
public class Product {
    @Id
    private String id;

    @Field("product_name")
    private String name;

    @Indexed
    private String category;

    private BigDecimal price;

    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;
}

Embedded Documents

@Document(collection = "orders")
public class Order {
    @Id
    private String id;

    private Customer customer;           // Embedded
    private List<OrderItem> items;       // Embedded list
    private Address shippingAddress;     // Embedded
}

// No @Document - embedded class
public class OrderItem {
    private String productId;
    private String productName;
    private int quantity;
    private BigDecimal price;
}

References

@Document(collection = "posts")
public class Post {
    @Id
    private String id;
    private String title;

    @DBRef
    private User author;                 // Lazy loaded reference

    // Manual reference (preferred for performance)
    private String authorId;
}

Repository Pattern

Basic Repository

public interface ProductRepository extends MongoRepository<Product, String> {

    // Derived queries
    List<Product> findByCategory(String category);
    List<Product> findByPriceLessThan(BigDecimal price);
    List<Product> findByCategoryAndPriceBetween(
        String category, BigDecimal min, BigDecimal max);

    // Sorting
    List<Product> findByCategoryOrderByPriceDesc(String category);

    // Limiting
    List<Product> findTop5ByCategoryOrderByPriceAsc(String category);

    // Exists/Count
    boolean existsByName(String name);
    long countByCategory(String category);
}

@Query Annotation

public interface ProductRepository extends MongoRepository<Product, String> {

    @Query("{ 'category': ?0, 'price': { $lte: ?1 } }")
    List<Product> findByCategoryWithMaxPrice(String category, BigDecimal maxPrice);

    @Query("{ 'tags': { $in: ?0 } }")
    List<Product> findByAnyTag(List<String> tags);

    @Query("{ 'name': { $regex: ?0, $options: 'i' } }")
    List<Product> searchByName(String keyword);

    // Projection
    @Query(value = "{ 'category': ?0 }", fields = "{ 'name': 1, 'price': 1 }")
    List<Product> findNameAndPriceByCategory(String category);
}

Aggregation in Repository

@Aggregation(pipeline = {
    "{ $match: { status: 'COMPLETED' } }",
    "{ $group: { _id: '$customerId', total: { $sum: '$amount' } } }",
    "{ $sort: { total: -1 } }",
    "{ $limit: 10 }"
})
List<CustomerTotal> findTopCustomers();

MongoTemplate

CRUD Operations

@Service
@RequiredArgsConstructor
public class ProductService {
    private final MongoTemplate mongoTemplate;

    // Create
    public Product save(Product product) {
        return mongoTemplate.save(product);
    }

    // Insert (fails if exists)
    public Product insert(Product product) {
        return mongoTemplate.insert(product);
    }

    // Read
    public Product findById(String id) {
        return mongoTemplate.findById(id, Product.class);
    }

    public List<Product> findByCategory(String category) {
        Query query = Query.query(Criteria.where("category").is(category));
        return mongoTemplate.find(query, Product.class);
    }

    // Update
    public UpdateResult updatePrice(String id, BigDecimal price) {
        Query query = Query.query(Criteria.where("id").is(id));
        Update update = Update.update("price", price);
        return mongoTemplate.updateFirst(query, update, Product.class);
    }

    // Delete
    public DeleteResult delete(String id) {
        Query query = Query.query(Criteria.where("id").is(id));
        return mongoTemplate.remove(query, Product.class);
    }
}

Complex Queries

public List<Product> search(ProductFilter filter) {
    Query query = new Query();

    // Multiple criteria
    if (filter.getCategory() != null) {
        query.addCriteria(Criteria.where("category").is(filter.getCategory()));
    }

    if (filter.getMinPrice() != null && filter.getMaxPrice() != null) {
        query.addCriteria(Criteria.where("price")
            .gte(filter.getMinPrice())
            .lte(filter.getMaxPrice()));
    }

    // OR condition
    if (filter.getKeywords() != null) {
        query.addCriteria(new Criteria().orOperator(
            Criteria.where("name").regex(filter.getKeywords(), "i"),
            Criteria.where("description").regex(filter.getKeywords(), "i")
        ));
    }

    // Pagination
    query.with(PageRequest.of(filter.getPage(), filter.getSize()));

    // Sorting
    query.with(Sort.by(Sort.Direction.DESC, "createdAt"));

    // Projection
    query.fields().include("name", "price", "category");

    return mongoTemplate.find(query, Product.class);
}

Aggregation Framework

Basic Pipeline

public List<CategoryStats> getCategoryStats() {
    Aggregation agg = Aggregation.newAggregation(
        Aggregation.match(Criteria.where("active").is(true)),
        Aggregation.group("category")
            .count().as("count")
            .avg("price").as("avgPrice")
            .sum("stock").as("totalStock"),
        Aggregation.sort(Sort.Direction.DESC, "count")
    );

    return mongoTemplate.aggregate(agg, "products", CategoryStats.class)
        .getMappedResults();
}

Lookup (Join)

Aggregation agg = Aggregation.newAggregation(
    Aggregation.lookup("users", "userId", "_id", "user"),
    Aggregation.unwind("user"),
    Aggregation.project()
        .andInclude("orderNumber", "total")
        .and("user.name").as("customerName")
);

Unwind Arrays

Aggregation agg = Aggregation.newAggregation(
    Aggregation.unwind("items"),
    Aggregation.group("items.productId")
        .sum("items.quantity").as("totalSold")
        .first("items.productName").as("productName"),
    Aggregation.sort(Sort.Direction.DESC, "totalSold"),
    Aggregation.limit(10)
);

Indexes

Annotations

@Document(collection = "products")
@CompoundIndex(name = "category_price", def = "{'category': 1, 'price': -1}")
public class Product {

    @Indexed(unique = true)
    private String sku;

    @Indexed
    private String category;

    @TextIndexed(weight = 3)
    private String name;

    @TextIndexed
    private String description;

    @Indexed(expireAfter = "30d")
    private LocalDateTime expiresAt;
}

Programmatic

mongoTemplate.indexOps(Product.class).ensureIndex(
    new Index()
        .on("category", Sort.Direction.ASC)
        .on("price", Sort.Direction.DESC)
        .named("category_price_idx")
);

Update Operations

Update Operators

Update update = new Update()
    .set("name", "New Name")
    .inc("viewCount", 1)
    .push("tags", "new-tag")
    .addToSet("categories", "electronics")
    .unset("deprecatedField")
    .currentDate("lastModified");

mongoTemplate.updateFirst(query, update, Product.class);

Upsert

mongoTemplate.upsert(query, update, Product.class);

Bulk Operations

BulkOperations bulkOps = mongoTemplate.bulkOps(BulkMode.ORDERED, Product.class);
products.forEach(p -> bulkOps.insert(p));
bulkOps.execute();

Testing with Testcontainers

@DataMongoTest
@Testcontainers
class ProductRepositoryTest {

    @Container
    @ServiceConnection
    static MongoDBContainer mongo = new MongoDBContainer("mongo:7.0");

    @Autowired
    private ProductRepository repository;

    @Test
    void shouldFindByCategory() {
        repository.save(new Product("Phone", "electronics", BigDecimal.valueOf(999)));

        List<Product> found = repository.findByCategory("electronics");

        assertThat(found).hasSize(1);
    }
}

Common Query Methods

MethodMongoDB Equivalent
findByX(x){ x: x }
findByXAndY(x, y){ x: x, y: y }
findByXOrY(x, y){ $or: [{x: x}, {y: y}] }
findByXBetween(a, b){ x: { $gte: a, $lte: b } }
findByXLessThan(x){ x: { $lt: x } }
findByXIn(list){ x: { $in: list } }
findByXRegex(pattern){ x: { $regex: pattern } }
findByXExists(bool){ x: { $exists: bool } }

When NOT to Use This Skill

  • Raw MongoDB driver - Use mongodb skill for driver-level operations
  • Relational data - Use spring-data-jpa or spring-data-jdbc
  • Full-text search focus - Consider spring-data-elasticsearch
  • Graph relationships - Use spring-data-neo4j

Anti-Patterns

Anti-PatternProblemSolution
Using @DBRef everywhereN+1 queries, slowEmbed or manual references
Missing indexesSlow queriesAdd @Indexed, compound indexes
Fetching full documentsWasted bandwidthUse projections, fields()
Transactions without replica setTransactions failConfigure replica set
Documents > 16MBInsert failsRedesign, use GridFS for large files
Dynamic field typesQuery issuesUse consistent schemas

Quick Troubleshooting

ProblemDiagnosticFix
Connection refusedCheck MongoDB runningStart MongoDB, check URI
Duplicate key errorCheck @Id or unique indexHandle or use upsert
Query returns emptyCheck field namesVerify @Field mapping matches DB
Slow aggregationCheck stagesAdd $match early, use indexes
Transaction failsCheck replica setConfigure replica set or remove transaction

Reference Documentation

Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

skills/databases/spring-data-mongodb

Default branch

main

Latest commit

9496306

Tree SHA

fe4e2f1