Spring Data MongoDB provides repository support and template-based access for MongoDB, treating documents as domain objects.

Setup

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
  
  spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/myapp
      # or individual properties:
      # host: localhost
      # port: 27017
      # database: myapp
  

Document Entity

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

    private String name;
    private BigDecimal price;
    private List<String> tags;

    @Indexed(unique = true)
    private String sku;

    @CreatedDate
    private Instant createdAt;
}
  

Repository

  public interface ProductRepository extends MongoRepository<Product, String> {
    List<Product> findByTagsContaining(String tag);
    List<Product> findByPriceBetween(BigDecimal min, BigDecimal max);
    Optional<Product> findBySku(String sku);

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

MongoTemplate for Complex Queries

  @Service
public class ProductService {
    private final MongoTemplate mongoTemplate;

    public List<Product> findByTagsAndPriceRange(List<String> tags, BigDecimal maxPrice) {
        Query query = new Query();
        query.addCriteria(Criteria.where("tags").in(tags)
            .and("price").lte(maxPrice));
        query.with(Sort.by(Sort.Direction.DESC, "createdAt"));
        return mongoTemplate.find(query, Product.class);
    }

    public void updatePrice(String id, BigDecimal newPrice) {
        Query query = Query.query(Criteria.where("id").is(id));
        Update update = new Update().set("price", newPrice).currentDate("updatedAt");
        mongoTemplate.updateFirst(query, update, Product.class);
    }
}
  

Aggregation Pipeline

  Aggregation agg = Aggregation.newAggregation(
    Aggregation.match(Criteria.where("category").is("electronics")),
    Aggregation.group("brand").avg("price").as("avgPrice").count().as("count"),
    Aggregation.sort(Sort.Direction.DESC, "avgPrice")
);
AggregationResults<BrandStats> results = mongoTemplate.aggregate(agg, "products", BrandStats.class);
  

Indexing

  @Document(collection = "events")
@CompoundIndex(def = "{'userId': 1, 'timestamp': -1}")
public class Event {
    @Id
    private String id;
    @Indexed
    private Long userId;
    private Instant timestamp;
    private String type;
}
  

Transactions (Replica Set Required)

  @Transactional
public void transferInventory(String fromId, String toId, int quantity) {
    Product from = productRepository.findById(fromId).orElseThrow();
    Product to = productRepository.findById(toId).orElseThrow();
    from.setStock(from.getStock() - quantity);
    to.setStock(to.getStock() + quantity);
    productRepository.save(from);
    productRepository.save(to);
}
  

Best Practices

  • Design documents around query patterns (denormalize when needed)
  • Create indexes for frequently queried fields
  • Use @CompoundIndex for multi-field queries
  • Prefer repository methods for simple queries; MongoTemplate for complex ones
  • Use aggregation pipeline for analytics and reporting queries