Enterprise-grade RAG backend service built with Spring Boot and LangChain4j
This is the core backend service of OpenContext, implementing document processing pipelines, hybrid search capabilities, and MCP protocol APIs for secure, self-hosted RAG systems.
The core service follows a layered architecture with clear separation of concerns:
com.opencontext/
├── config/ # Spring configuration classes
├── common/ # Common response objects and utilities
├── controller/ # REST API endpoints
├── service/ # Business logic layer
├── repository/ # Data access layer with QueryDSL
├── entity/ # JPA entities
├── dto/ # Data transfer objects
├── enums/ # Enum definitions (IngestionStatus, ErrorCode)
├── exception/ # Custom exceptions and global handlers
└── pipeline/ # Document processing pipeline components
- DocumentParsingService: Integrates with Unstructured.io for PDF/Markdown parsing
- ChunkingService: Hierarchical text chunking with LangChain4j
- EmbeddingService: Ollama API integration for text embeddings
- IndexingService: Elasticsearch indexing with hybrid search setup
- SearchService: Hybrid BM25 + vector search with Korean language support
- ContentRetrievalService: Chunk content retrieval with token limit handling
- SearchController: MCP protocol API endpoints (
/search,/get-content)
- PostgreSQL: Source document metadata and chunk hierarchy storage
- Elasticsearch: Document search index with Korean Nori analyzer
- MinIO: Original file storage with S3-compatible API
- Java 21: Latest LTS with virtual thread support
- Spring Boot 3.3.11: Main application framework
- Spring Security: API key authentication
- Spring Data JPA + Hibernate 6.x: ORM with PostgreSQL
- QueryDSL 5.x: Type-safe query construction
- LangChain4j 0.36.2: RAG pipeline orchestration framework
- Ollama: Local embedding model serving (Qwen3-Embedding-0.6B)
- Elasticsearch 8.x: Hybrid search engine with vector capabilities
- Unstructured.io: Document parsing service (Docker API)
- JUnit 5: Unit testing framework
- TestContainers: Integration testing with real databases
- Mockito: Mocking framework
- Flyway: Database migration management
These endpoints are used by the MCP adapter for AI assistant integration:
# Phase 1: Exploratory search
GET /api/v1/search?query={query}&topK={count}
# Phase 2: Content retrieval
POST /api/v1/get-content
Content-Type: application/json
{
"chunkId": "uuid-string",
"maxTokens": 25000
}Document management endpoints requiring X-API-KEY header:
# Upload documents
POST /api/v1/sources/upload
X-API-KEY: your-api-key
Content-Type: multipart/form-data
# List documents with pagination
GET /api/v1/sources?page=0&size=20&sort=createdAt,desc
X-API-KEY: your-api-key
# Trigger document reprocessing
POST /api/v1/sources/{id}/resync
X-API-KEY: your-api-key
# Delete document
DELETE /api/v1/sources/{id}
X-API-KEY: your-api-key# application.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/opencontext
username: user
password: password
jpa:
hibernate:
ddl-auto: validate # Production: validate only
show-sql: false
flyway:
baseline-on-migrate: trueapp:
elasticsearch:
url: http://localhost:9200
index: document_chunks_indexapp:
ollama:
api:
url: http://localhost:11434
embedding:
model: dengcao/Qwen3-Embedding-0.6B:F16
embedding:
batch-size: 10opencontext:
api:
key: dev-api-key-123 # Change for productionStores original document metadata and processing status:
CREATE TABLE source_documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
original_filename VARCHAR(255) NOT NULL,
file_storage_path VARCHAR(1024) NOT NULL,
file_type VARCHAR(50) NOT NULL,
file_size BIGINT NOT NULL,
file_checksum VARCHAR(64) NOT NULL UNIQUE,
ingestion_status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
error_message TEXT,
last_ingested_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);Maintains hierarchical chunk structure and relationships:
CREATE TABLE document_chunks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
source_document_id UUID NOT NULL REFERENCES source_documents(id) ON DELETE CASCADE,
parent_chunk_id UUID REFERENCES document_chunks(id) ON DELETE CASCADE,
sequence_in_document INT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);Document chunks are stored in Elasticsearch with the following structure:
{
"mappings": {
"properties": {
"chunkId": { "type": "keyword" },
"content": {
"type": "text",
"analyzer": "korean_nori"
},
"embedding": {
"type": "dense_vector",
"dims": 1024,
"index": "true",
"similarity": "cosine"
},
"metadata": {
"properties": {
"title": { "type": "text", "analyzer": "korean_nori" },
"hierarchyLevel": { "type": "integer" },
"originalFilename": { "type": "keyword" },
"fileType": { "type": "keyword" }
}
}
}
}
}java --version # OpenJDK 21
./gradlew --version # Gradle 8+# Start dependencies with Docker Compose
docker compose up -d postgres elasticsearch ollama minio
# Run application
./gradlew bootRun
# Run with specific profile
./gradlew bootRun --args='--spring.profiles.active=dev'# Unit tests
./gradlew test
# Integration tests (requires TestContainers)
./gradlew integrationTest
# Test with coverage
./gradlew jacocoTestReport
# Build without tests
./gradlew build -x test# Apply migrations
./gradlew flywayMigrate
# Check migration status
./gradlew flywayInfo
# Repair migration checksums (if needed)
./gradlew flywayRepairThe application exposes Spring Boot Actuator endpoints:
# Overall health status
curl http://localhost:8080/actuator/health
# Database connectivity
curl http://localhost:8080/actuator/health/db
# Elasticsearch connectivity
curl http://localhost:8080/actuator/health/elasticsearch
# Application info
curl http://localhost:8080/actuator/info# application.yml
logging:
level:
com.opencontext: DEBUG # Application logs
org.hibernate.SQL: DEBUG # SQL queries
org.hibernate.orm.jdbc.bind: TRACE # SQL parameters# application.yml
spring:
datasource:
hikari:
maximum-pool-size: 10
minimum-idle: 2
connection-timeout: 20000
server:
tomcat:
threads:
max: 200
min-spare: 10The application uses structured error responses with specific error codes:
VALIDATION_FAILED(400): Request validation errorsINSUFFICIENT_PERMISSION(403): Invalid API keySOURCE_DOCUMENT_NOT_FOUND(404): Document not foundCHUNK_NOT_FOUND(404): Chunk not foundDUPLICATE_FILE_UPLOADED(409): File already existsCONTENT_NOT_AVAILABLE(422): Chunk content unavailableEXTERNAL_SERVICE_UNAVAILABLE(503): Ollama/Elasticsearch connection error
{
"success": false,
"data": null,
"message": "Human-readable error message",
"errorCode": "ERROR_CODE_ENUM_VALUE",
"timestamp": "2025-08-21T12:00:00Z"
}- All administrative endpoints require
X-API-KEYheader - Default development key:
dev-api-key-123 - Generate production keys with:
openssl rand -hex 32
- All data processing happens locally
- No external API calls for sensitive document content
- PostgreSQL stores only metadata and relationships
- Elasticsearch contains indexed content for search
- MCP endpoints (
/search,/get-content) have no authentication for internal use - Administrative endpoints require API key authentication
- Configure reverse proxy (nginx) for HTTPS termination in production
Elasticsearch Connection Failed
# Check Elasticsearch status
curl http://localhost:9200/_cluster/health
# Verify Korean analyzer plugin
curl http://localhost:9200/_cat/plugins?vOllama Model Not Found
# Pull embedding model
docker compose exec ollama ollama pull dengcao/Qwen3-Embedding-0.6B:F16
# List available models
docker compose exec ollama ollama listDatabase Migration Issues
# Check migration status
./gradlew flywayInfo
# Repair checksums after manual changes
./gradlew flywayRepair- Follow Google Java Style Guide
- Use Lombok annotations for boilerplate reduction
- Prefer constructor injection over field injection
- Write comprehensive JavaDoc for public APIs
- Unit tests for all service classes
- Integration tests using TestContainers for repository classes
- API tests for all controller endpoints
- Minimum 80% code coverage required
- Use conventional commits format
- Include issue numbers in commit messages
- Keep commits focused on single concerns
- Write clear, descriptive commit messages
For more information, see the main Contributing Guide.