The Chenile story · Part 11 of 13

Chenile Query: metadata-driven search over MyBatis

Describe a query's columns in JSON, pair it with a MyBatis mapper, and Chenile gives you a filter/sort/paginate/download REST endpoint — with no Java code — from the chenile-query-workflow-blueprints repo.

CQRS, the easy way

Chenile separates the command side (services and workflows that change state) from the query side (read-optimized search). Chenile Query — in chenile-query-workflow-blueprints — is the read side: a metadata-driven search framework backed by MyBatis.

The headline is what you don’t write. To add a searchable query you configure two files and write no Java:

  1. a MyBatis mapper (the SQL), and
  2. a JSON metadata file describing the queryable columns.

Chenile turns that into a REST endpoint that supports filtering, like/contains matches, sorting, pagination, downloads and canned reports.

The query metadata

Each column is declared once, with the capabilities it should expose:

{
  "id": "Student.getAll",
  "name": "students",
  "columnMetadata": {
    "name":   { "name": "name",   "columnType": "Text", "filterable": true, "likeQuery": true },
    "branch": { "name": "branch", "columnType": "Text", "filterable": true, "containsQuery": true, "sortable": true },
    "percentage": { "name": "percentage", "columnType": "Number", "filterable": true, "sortable": true }
  }
}

filterable, likeQuery, containsQuery and sortable are the switches that decide what a caller may do with each column — the framework enforces them so clients can’t craft arbitrary queries.

Try it

This is the query builder in miniature. Toggle the filters and sort; watch the SearchRequest Chenile would build, and the rows it returns — no Java involved:

Behind the scenes a SearchRequest flows through the SearchService to a MyBatis-backed QueryStore, and comes back as a paginated SearchResponse (SearchPaginationInfo, SortCriterion, ResponseRow). Downloads (DownloadFormat) and saved canned reports (CannedReport) come along for free.

No code — generated by blueprint

Because a query is just configuration, the mybatisQuery blueprint generates the whole module for you: a project with the mapper and metadata folders (by convention, …query.service.mapper) that depends on chenile-query-controller. The student-query-service and order-query-service projects in chenile-samples are complete working examples — open them and note that src/main/java is essentially empty.

Built for many tenants

The query side is tenant-aware out of the box. A QueryTenantResolver resolves the current tenant, and QueryDatasourcesProperties lets each tenant read from its own query datasource — so the same endpoint serves every customer while each sees only its own rows. That’s the thread we pick up in part 13.

Where to look
Model & service: QueryMetadata, ColumnMetadata, SearchRequest/SearchResponse, SearchService, QueryStore in query-api / chenile-query-service; the endpoint in chenile-query-controller. Samples: student-query-service, order-query-service.