https://github.com/thekitchencoder/jspec
Lightweight Java library for evaluating business criteria against JSON/YAML using MongoDB-style operators
https://github.com/thekitchencoder/jspec
business-rules criteria evaluation java-21 json mongodb specification yaml
Last synced: 9 days ago
JSON representation
Lightweight Java library for evaluating business criteria against JSON/YAML using MongoDB-style operators
- Host: GitHub
- URL: https://github.com/thekitchencoder/jspec
- Owner: thekitchencoder
- License: mit
- Created: 2025-11-14T10:52:09.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2026-06-03T23:00:39.000Z (21 days ago)
- Last Synced: 2026-06-04T02:04:00.871Z (21 days ago)
- Topics: business-rules, criteria, evaluation, java-21, json, mongodb, specification, yaml
- Language: Java
- Homepage:
- Size: 636 KB
- Stars: 0
- Watchers: 0
- Forks: 1
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# JSPEC
[](https://search.maven.org/search?q=g:%22uk.codery%22%20AND%20a:%22jspec%22)
[](https://github.com/thekitchencoder/jspec/actions/workflows/build.yml)
[](https://codecov.io/gh/thekitchencoder/jspec)
[](https://opensource.org/licenses/MIT)
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fthekitchencoder%2Fjspec?ref=badge_shield)
[](https://openjdk.org/projects/jdk/21/)
[](https://javadoc.io/doc/uk.codery/jspec)
JSPEC (JSON Specification Evaluator) is a lightweight, Spring-independent Java library for evaluating business criteria against JSON/YAML documents with MongoDB-style operators.
## Features
- **23 query operators** – Comparison, collection, advanced, string, date/range, and logical (`$and`/`$or`/`$not`) checks, using familiar MongoDB-style query syntax.
- **Tri-state evaluation** – Every criterion reports `MATCHED`, `NOT_MATCHED`, or `UNDETERMINED` for precise diagnostics.
- **Graceful error handling** – Bad data, unknown operators, or type mismatches never halt the evaluation pipeline.
- **Thread-safe by design** – Immutable records and parallel execution make large specifications fast and safe.
- **Deep document navigation** – Dot notation walks arbitrarily nested JSON/YAML payloads.
- **Zero framework dependencies** – Works in plain Java applications or alongside Spring.
- **Java 21 foundation** – Modern records, switch expressions, and immutable collections throughout.
- **Context-document references** – `$contextPath` operand sentinel lets one specification be scored against many context documents.
## Quick Start
### Installation
Add the dependency to your `pom.xml`:
```xml
uk.codery
jspec
0.5.2
```
### Basic Usage
1. **Define a `Specification`** containing your criteria. You can write it as YAML/JSON or construct it programmatically. Each entry is auto-typed: a `query` map becomes a `QueryCriterion`, a `junction` block becomes a `CompositeCriterion`, and `ref` entries reference previously defined criteria (so they only evaluate once).
```yaml
# specification.yaml
id: order-validation
criteria:
- id: minimum-order
query:
order.total:
$gte: 25.00
- id: customer-verified
query:
customer.verified: true
- id: express-shipping-eligible
junction: AND
criteria:
- ref: minimum-order
- ref: customer-verified
```
2. **Instantiate the evaluator** and inspect the tri-state results.
```java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import java.io.File;
import java.util.Map;
import uk.codery.jspec.evaluator.SpecificationEvaluator;
import uk.codery.jspec.model.Specification;
import uk.codery.jspec.result.EvaluationOutcome;
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
Specification spec = mapper.readValue(new File("specification.yaml"), Specification.class);
SpecificationEvaluator evaluator = new SpecificationEvaluator(spec);
Map document = Map.of(
"order", Map.of("total", 50.00),
"customer", Map.of("verified", true)
);
EvaluationOutcome outcome = evaluator.evaluate(document);
System.out.println("Summary: " + outcome.summary());
outcome.results().forEach(result ->
System.out.printf(" - %s -> %s%n", result.id(), result.state())
);
```
## Context-Document References
`$contextPath` is an operand sentinel that defers a value lookup to a separately-supplied context document. Reach for it when the same specification needs to be evaluated against many context documents — for example, scoring a single claim against each of several candidate identities — so the criteria stay templated and only the context changes per call.
```java
import java.util.Map;
import uk.codery.jspec.evaluator.SpecificationEvaluator;
import uk.codery.jspec.model.QueryCriterion;
import uk.codery.jspec.model.Specification;
import uk.codery.jspec.result.EvaluationOutcome;
Specification spec = new Specification("same-email", java.util.List.of(
new QueryCriterion("match",
Map.of("email", Map.of("$eq", Map.of("$contextPath", "candidate.email"))))));
SpecificationEvaluator evaluator = new SpecificationEvaluator(spec);
Map target = Map.of("email", "a@b.com");
Map context = Map.of("candidate", Map.of("email", "a@b.com"));
EvaluationOutcome outcome = evaluator.evaluate(target, context);
// outcome.summary().matched() == 1
```
If a `$contextPath` operand fails to resolve, the containing criterion becomes `UNDETERMINED` and the unresolved path is recorded as `context.` in the result's `missingPaths` — consistent with how target-document misses are surfaced.
A path whose final segment is present-but-`null` resolves successfully and yields `null` to the operator (so `$eq: null` works); a path with a missing entry is unresolved and short-circuits to `UNDETERMINED`. Omit a key entirely if you want the criterion to be `UNDETERMINED` rather than compared against `null`.
The sentinel works identically in YAML specifications — just write `$contextPath: candidate.email` wherever an operand value would appear.
## Documentation
For deeper dives, read the docs in `docs/`:
- **[Supported Operators](docs/OPERATORS.md)** – Reference for all 23 query operators.
- **[Evaluation Model](docs/EVALUATION_MODEL.md)** – How the tri-state pipeline surfaces errors without throwing.
- **[Architecture](docs/ARCHITECTURE.md)** – Core records, evaluators, and thread-safety guidance.
- **[General Use Cases](docs/USECASES.md)** – Practical scenarios for embedding JSPEC.
- **[AI Use Cases](docs/AI_USECASES.md)** – Synthesized guidance for guardrails, hybrid-symbolic patterns, and agent routing.
- **[JavaDoc](https://javadoc.io/doc/uk.codery/jspec)** – Full API reference.
## Building from Source
To build the project locally:
```bash
# Clone the repository
git clone https://github.com/thekitchencoder/jspec.git
cd jspec
# Compile and run the full suite
mvn clean verify
# Fast feedback while iterating
mvn test
```
### Requirements
- Java 21 or higher
- Maven 3.6+
### Dependencies
- **Jackson DataFormat YAML** – JSON/YAML parsing for specifications and documents.
- **Lombok** – Annotation-driven boilerplate reduction during compilation.
- **SLF4J API** – Logging abstraction; bring your own binding.
## Demo
To run the demo application and see the engine in action:
```bash
mvn test-compile exec:java -Dexec.mainClass="uk.codery.jspec.demo.Main"
```
## Contributing
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
## License
This project is licensed under the MIT License - see [LICENSE](LICENSE) file for details.
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fthekitchencoder%2Fjspec?ref=badge_large)
## Support
For issues, questions, or suggestions:
- Open an issue on GitHub
- Review existing documentation
- Check the demo examples in `src/test/java/uk/codery/jspec/demo/`
## Acknowledgments
- Operator design inspired by MongoDB query language
- Tri-state evaluation model based on graceful degradation principles
- Built with modern Java 21 features for clean, maintainable code