How does the PartTree engine parse a repository method name? Walk through subject vs. predicate and the findBy/countBy/existsBy/deleteBy verbs.
answer
- split at first By: subject vs predicate
- predicate -> OrPart -> Part
- subject verbs: find/count/exists/delete + Distinct/Top
- Part.Type.getNumberOfArguments()
- greedy property resolution, _ forces split
basics
~20 sPartTree splits the name at the first By into a subject (the verb: find/count/exists/delete) and a predicate. It then breaks the predicate on Or into OR-parts and each on And into Parts, mapping every Part to a property and keyword.
solid answer
~40 sPartTree (Spring Data Commons, org.springframework.data.repository.query.parser) is the parser behind every derived method. It takes the method name and the entity type and produces a tree. First it separates the subject — the introducing clause up to By: find/read/get/query/search/stream (return entities), count (long), exists (boolean), delete/remove (delete) — also recognizing Distinct and Top/First limiting. The remaining predicate is split on Or into OrPart objects; each OrPart is split on And into Part objects. Each Part resolves a property path against the entity's metadata and captures a keyword type (SIMPLE_PROPERTY, BETWEEN, LIKE, IS_NULL, IN, etc.) plus how many bind parameters it needs. A trailing OrderBy becomes a Sort. Because PartTree is store-agnostic, each module (JPA, Mongo...) walks this tree to emit its native query; the parsing rules are identical everywhere.
code
java · 17 lines// Conceptually, what PartTree derives from a method name:
// String name = "findDistinctByLastNameAndAgeGreaterThanOrderByAgeDesc";
// PartTree tree = new PartTree(name, Person.class);
//
// tree.isDistinct() -> true
// tree.getSort() -> Sort.by("age").descending()
// Iterating OrParts -> one OrPart -> two Parts:
// Part(lastName, SIMPLE_PROPERTY, args=1)
// Part(age, GREATER_THAN, args=1)
public interface PersonRepository extends CrudRepository<Person, Long> {
List<Person> findDistinctByLastNameAndAgeGreaterThanOrderByAgeDesc(String lastName, int minAge);
// existsBy subject -> boolean; deleteBy subject -> delete operation
boolean existsByEmail(String email);
long deleteByLastName(String lastName);
}go deeper
Aware the method name is parsed; not expected to name PartTree internals.
Can describe subject vs predicate and the main verbs.
Must articulate the OrPart/Part tree, Part.Type arity, greedy resolution, and underscore disambiguation.
Leverages PartTree's Commons-level position to reason about cross-store consistency and metadata-driven validation.
**What PartTree is.** `PartTree` (`org.springframework.data.repository.query.parser.PartTree`) is the shared grammar engine in Spring Data *Commons*. Given a method name and the domain type, its constructor `new PartTree(methodName, domainType)` produces an iterable tree that store modules interpret. It is the single source of truth for the naming grammar, which is why the rules are identical across JPA, MongoDB, Redis, etc. **Step 1 — split subject and predicate.** PartTree matches a leading *introducing clause* against a set of prefixes and takes everything up to the first standalone `By` as the **subject**; the rest is the **predicate**. The subject determines the *operation*: - `find` / `read` / `get` / `query` / `search` / `stream` → return matching entities. - `count` → return a count (`long`). - `exists` → return `boolean`. - `delete` / `remove` → delete matching rows (return type may be `void`, a count, or the removed entities depending on the module). The subject also parses modifiers: `Distinct` (`findDistinctBy…`) and limiting via `Top`/`First` with an optional number (`findTop3By…`, `findFirstBy…`). PartTree exposes flags like `isCountProjection()`, `isDelete()`, `isExistsProjection()`, `isDistinct()`, and `getMaxResults()`. **Step 2 — OR split.** The predicate is split on the `Or` keyword into a list of `OrPart` objects. Semantically these are OR-ed together. **Step 3 — AND split.** Each `OrPart` is split on `And` into individual `Part` objects, AND-ed together. **Step 4 — build each Part.** For each `Part`, PartTree greedily consumes the longest property path it can resolve, then interprets any trailing keyword to assign a `Part.Type` (e.g. `SIMPLE_PROPERTY`, `BETWEEN`, `LIKE`, `IN`, `IS_NULL`, `GREATER_THAN`, `STARTING_WITH`). Each `Type` declares `getNumberOfArguments()`, which is how PartTree knows how many method parameters the Part binds (0 for `IS_NULL`, 2 for `BETWEEN`, 1 otherwise). This is what the store uses to line up bind values. **Step 5 — sort.** A trailing `OrderBy…` clause is parsed into a `Sort`, retrievable via `PartTree.getSort()`. **Property resolution & greediness.** Resolution is greedy: for `findByAddressZipCode`, PartTree first tries the whole tail as one property, then backtracks, splitting into path segments (`address.zipCode`). Ambiguity — where `AddressZip` could be property `addressZip` or nested `address.zip` — is resolved greedily toward the longest matching property; you force a specific split with an underscore (`findByAddress_ZipCode`). Unresolvable paths throw `PropertyReferenceException` at bootstrap. **Why it matters in an interview.** Knowing the subject/predicate/OrPart/Part structure explains *why* keyword arity matters, *why* Or can't be nested, *why* bad names fail at startup, and *why* the same method name behaves consistently across stores — all of it flows from this one Commons parser.
- The engine can't decide between property userAddress and nested user.address for findByUserAddress. How do you disambiguate?Insert an underscore to mark the split point: findByUser_Address forces the user.address nested path; without it PartTree resolves greedily toward the longest single property (userAddress).
- Where does PartTree live, and why does that matter for multi-store projects?It lives in Spring Data Commons, not in a store module. So the naming grammar is identical across JPA/Mongo/Redis; each module only supplies the translation of the parsed tree into its native query.
saying these in an interview costs you the question
- Thinking each store (JPA, Mongo) has its own separate name parser.
- Believing the subject is always 'find' and only the predicate matters.
- Not knowing Or splits before And (OrPart contains Parts).
- Assuming underscores in names are illegal rather than a disambiguation tool.