Skip to main content

Developer Guide

Whether you're building a search experience on top of Turing ES or contributing to the project itself, this guide has everything you need to get up and running quickly.

Turing ES is a fully open-source enterprise search platform with semantic navigation and GenAI capabilities. The source code lives at github.com/openviglet/turing and all contributions are welcome.


Tech Stack

Understanding the stack helps you navigate the codebase and decide where to plug in.

LayerTechnology
BackendJava 21 · Spring Boot · Spring AI
Search EngineApache Solr (primary) · Elasticsearch · Lucene
Message QueueApache Artemis
DatabaseH2 (dev) · PostgreSQL / MySQL (prod)
FrontendReact · TypeScript · shadcn/ui · Vite
AI / GenAISpring AI · ChromaDB · PgVector · Milvus
BuildMaven (backend) · npm (frontend)
CI/CDGitHub Actions

Setting Up Your Dev Environment

Prerequisites

Before you begin, make sure you have these installed:

Clone the Repository

git clone https://github.com/openviglet/turing.git
cd turing

Start Services with Docker Compose

Apache Solr is the only external service required to run Turing ES. Apache Artemis runs embedded inside the application and requires no separate installation. The easiest way to start Solr locally is with Docker Compose:

docker-compose up -d

This starts:

  • Apache Solr at http://localhost:8983
tip

Wait for Solr to be fully ready before starting the backend. You can check at http://localhost:8983/solr/#/.


Running Turing ES

Backend (Spring Boot)

Run the full backend including the bundled React console:

cd turing
mvn spring-boot:run -pl turing-app

Or skip the npm build step if you haven't changed the frontend:

mvn spring-boot:run -pl turing-app -Dskip.npm

The backend starts at http://localhost:2700.

Frontend — React Admin Console

For active frontend development, run the React dev server separately. First start the backend in headless mode:

mvn spring-boot:run -pl turing-app -Dskip.npm -Dspring-boot.run.profiles=dev-ui

Then launch the React app:

cd turing/turing-ui
npm install
npm run dev

The Vite dev server starts at http://localhost:5173 with hot-reload enabled.

Production Build

cd turing
mvn clean install
mvn package -pl turing-app

The resulting JAR in turing-app/target/ bundles both the backend and the compiled React assets.


Development URLs

ServiceURLNotes
Admin Consolehttp://localhost:2700Backend-served
React Dev Serverhttp://localhost:5173Vite hot-reload
SN Search Samplehttp://localhost:2700/sn/Sample
Swagger UIhttp://localhost:2700/swagger-ui.htmlInteractive API docs
OpenAPI Spechttp://localhost:2700/v3/api-docsJSON spec
Solrhttp://localhost:8983Docker Compose
Default credentials

On first startup, set the admin password via the environment variable TURING_ADMIN_PASSWORD. See the Installation Guide for details.


Java SDK

The Turing Java SDK lets you integrate semantic search into any JVM application.

Run the Sample

cd turing-java-sdk
mvn package
java -cp build/libs/turing-java-sdk-all.jar com.viglet.turing.client.sn.sample.TurSNClientSample

Add to Your Project via Maven Central

Add to your pom.xml:

<dependency>
<groupId>com.viglet.turing</groupId>
<artifactId>turing-java-sdk</artifactId>
<version>2026.1.17</version>
</dependency>

The SDK and turing-commons are published to Maven Central. No custom repository configuration is needed.

Build the SDK

cd turing-java-sdk
mvn package

Database Migrations with Liquibase

Turing ES uses Liquibase (v5.0.2) for database schema management. All schema changes — tables, columns, indexes, foreign keys — are tracked as versioned changelogs and applied automatically on startup. This ensures every environment (dev, staging, production) runs the exact same schema, regardless of the database platform.

How it works

When Turing ES starts, Spring Boot runs Liquibase before the application context is fully initialized. Liquibase reads the master changelog, compares it against the DATABASECHANGELOG tracking table, and applies any pending changesets. This means:

  • First startup — all migrations run, creating the full schema from scratch
  • Upgrades — only new changesets (added since the last startup) are applied
  • Rollbacks — changesets already applied are skipped (DATABASECHANGELOG tracks what has run)

Configuration

Liquibase is configured in application.yaml:

spring:
liquibase:
change-log: classpath:db/changelog/db.changelog-master.yaml
enabled: true
default-schema: PUBLIC

Changelog structure

All changelogs are in YAML format under turing-app/src/main/resources/db/changelog/:

db/changelog/
├── db.changelog-master.yaml ← master file, includes all others
├── v2025.3_baseline.yaml ← initial schema (full baseline)
├── v2026.1_update.yaml ← major version update
├── v2026.1_type_enum_fix.yaml ← type/enum corrections
├── v2026.1.11.yaml ← incremental patch
├── v2026.1.11.1.yaml ← incremental patch
├── v2026.1.12.yaml ← incremental patch
├── v2026.1.14.0.yaml ← incremental patch
├── v2026.1.14.1.yaml ← ...
├── ...
└── v2026.1.14.8.yaml

The master changelog (db.changelog-master.yaml) includes all files in sequential order:

databaseChangeLog:
- include:
file: db/changelog/v2025.3_baseline.yaml
- include:
file: db/changelog/v2026.1_update.yaml
- include:
file: db/changelog/v2026.1_type_enum_fix.yaml
- include:
file: db/changelog/v2026.1.11.yaml
# ... incremental patches follow

Naming convention

PatternExampleUsage
v{version}_baseline.yamlv2025.3_baseline.yamlFull schema baseline for a major version
v{version}_update.yamlv2026.1_update.yamlGenerated diff between two versions
v{version}.yamlv2026.1.12.yamlIncremental patch for a specific release

Writing a new migration

When adding a new table, column, or index, create a new changelog file following the naming convention and add it to the master changelog.

Example — adding a column to an existing table:

databaseChangeLog:
- changeSet:
id: v2026.1.15-01-add-priority-to-spotlight
author: viglet-team
preConditions:
- onFail: MARK_RAN
- tableExists:
tableName: sn_site_spotlight
- not:
- columnExists:
tableName: sn_site_spotlight
columnName: priority
changes:
- addColumn:
tableName: sn_site_spotlight
columns:
- column:
name: priority
type: INT
defaultValueNumeric: 0

Key rules:

  • Always use preconditions with onFail: MARK_RAN to make changesets idempotent — safe to re-run if the change was already applied manually
  • Changeset IDs use the format v{version}-{sequence}-{description} (e.g., v2026.1.14-01-add-default-label-to-custom-facet)
  • Authors use viglet-team for manual migrations or {name} (generated) for auto-generated diffs
  • Never modify an already-released changeset — always create a new one

Generating a diff changelog

The Liquibase Maven plugin can auto-generate a diff between two database versions:

cd turing-app
mvn liquibase:diff

This compares the reference database (current code) against the target database (previous version) and writes a changelog to src/main/resources/db/changelog/v2026.1_update.yaml. The diff tracks: tables, columns, indexes, foreign keys, and primary keys.

The plugin configuration is in turing-app/pom.xml and uses liquibase.properties for connection settings.

Database platform support

Liquibase abstracts SQL differences across platforms. The same changelogs work on all supported databases:

DatabaseDriverNotes
H2org.h2.DriverDefault for development — embedded, zero setup
MariaDB / MySQLorg.mariadb.jdbc.DriverProduction recommended
PostgreSQLorg.postgresql.DriverProduction recommended
Oracleoracle.jdbc.OracleDriverEnterprise environments
SQL Servercom.microsoft.sqlserver.jdbc.SQLServerDriverEnterprise environments

Liquibase handles type mappings (e.g., VARCHAR(255) on MySQL vs VARCHAR2(255) on Oracle) transparently.


Code Quality

Turing ES maintains high code quality standards. You can check the project health at any time:

ToolLink
SonarCloudsonarcloud.io/organizations/viglet-turing
GitHub Actionsopenviglet/turing/actions
GitHub Securityopenviglet/turing/security
Codecovapp.codecov.io/gh/openviglet/turing

Search Engine Plugin Architecture

Turing ES uses a plugin architecture to support multiple search backends behind a unified interface. This abstraction allows the same application code to work with Apache Solr, Elasticsearch, or Lucene — the active plugin is resolved at runtime based on the vendor configured per Search Engine instance.

The TurSearchEnginePlugin interface

All backend operations are defined in a single interface with four categories:

Search

MethodDescription
retrieveSearchResults()Execute a full-text query and return results with facets
retrieveFacetResults()Return facet counts for a query

Index management

MethodDescription
createIndex()Create a new core / index
deleteIndex()Delete a core / index
clearIndex()Remove all documents from a core without deleting it
listIndexes()List all cores / indices on the backend

Schema management

MethodDescription
addOrUpdateField()Add or update a field definition in the schema
deleteField()Remove a field from the schema
fieldExists()Check whether a field exists in the schema

Documents & monitoring

MethodDescription
indexDocument()Index a single document
deIndex()Remove a document from the index
commit()Flush pending writes (for backends that require explicit commits)
getDocumentTotal()Return the total number of indexed documents
getSystemInfo()Return backend version, OS, JVM, and memory information

Implementing a new backend

To add support for a new search engine:

  1. Create a class implementing TurSearchEnginePlugin
  2. Implement all methods in the four categories above
  3. Register the plugin in TurSearchEnginePluginFactory with a vendor identifier
  4. The factory resolves the correct plugin based on the vendor field of the TurSEInstance entity
Fallback behaviour

If a vendor is unrecognised or unavailable, TurSearchEnginePluginFactory falls back to the Solr plugin. Apache Solr is the primary supported backend with the most complete feature set.


REST API

Turing ES exposes a REST API for integrating search and AI capabilities into any application. All endpoints use JSON. Authentication uses the Key header with an API Token created in Administration → API Tokens.

For the full endpoint reference — search, autocomplete, spell check, latest searches, GenAI chat, and token usage — see REST API Reference.

For interactive exploration, use the built-in Swagger UI at http://localhost:2700/swagger-ui.html or the OpenAPI spec at http://localhost:2700/v3/api-docs.


Contributing

We'd love your help making Turing ES better. Here's how to get involved:

  1. Fork the openviglet/turing repository.
  2. Create a branch for your feature or fix: git checkout -b feature/my-improvement
  3. Commit your changes with clear, descriptive messages.
  4. Open a Pull Request — describe what you changed and why.

For larger contributions, open an issue first to discuss the approach before writing code.

tip

Check the open GitHub Issues for good first issues tagged with good first issue or help wanted.