REST dotazovací jazyk s RSQL

ODPOČINOK Najlepšie

Práve som oznámil nové Naučte sa jar kurz zameraný na základy jari 5 a Spring Boot 2:

>> SKONTROLUJTE KURZ Perzistencia hore

Práve som oznámil nové Naučte sa jar kurz zameraný na základy jari 5 a Spring Boot 2:

>> SKONTROLUJTE KURZ Tento článok je súčasťou série: • Dotazovací jazyk REST s kritériami Spring a JPA

• Dotazovací jazyk REST so špecifikáciami JPA Spring Data

• REST dotazovací jazyk s Spring Data JPA a Querydsl

• REST Query Language - Pokročilé vyhľadávacie operácie

• REST Query Language - implementácia ALEBO prevádzka

• Dotazovací jazyk REST s RSQL (aktuálny článok) • Dotazovací jazyk REST s webovou podporou Querydsl

1. Prehľad

V tomto piatom článku série ilustrujeme zostavenie dotazovacieho jazyka REST API pomocou skvelá knižnica - rsql-parser.

RSQL je nadstavba informačného kanála dotazu na položku (FIQL) - čistá a jednoduchá syntax filtra pre kanály; takže celkom prirodzene zapadá do REST API.

2. Prípravy

Najprv pridáme do knižnice závislosť maven:

 cz.jirutka.rsql rsql-parser 2.1.0 

A tiež definovať hlavnú entitu budeme pracovať s príkladmi - Používateľ:

@Entity verejná trieda Používateľ {@Id @GeneratedValue (strategy = GenerationType.AUTO) private Long id; private String meno; private String priezvisko; súkromný reťazcový e-mail; súkromný int vek; }

3. Analyzujte žiadosť

Spôsob, akým sú výrazy RSQL interne reprezentované, je vo forme uzlov a vzor návštevníka sa používa na analýzu vstupu.

Z tohto dôvodu ideme implementovať RSQLVisitor rozhranie a vytvoriť si vlastnú implementáciu pre návštevníkov - CustomRsqlVisitor:

verejná trieda CustomRsqlVisitor implementuje RSQLVisitor {private GenericRsqlSpecBuilder builder; public CustomRsqlVisitor () {builder = new GenericRsqlSpecBuilder (); } @Override public Specification visit (AndNode node, Void param) {return builder.createSpecification (node); } @Override public Specification visit (OrNode node, Void param) {return builder.createSpecification (node); } @Override public Specification visit (ComparisonNode node, Void params) {return builder.createSecification (node); }}

Teraz sa musíme vyrovnať s perzistenciou a zostaviť náš dotaz z každého z týchto uzlov.

Budeme používať špecifikácie Spring Data JPA, ktoré sme používali predtým - a budeme implementovať a Špecifikácia staviteľ z každého z týchto uzlov, ktoré navštívime, zostavte Špecifikácie:

verejná trieda GenericRsqlSpecBuilder {verejná špecifikácia createSpecification (uzol uzla) {if (uzol instanceof LogicalNode) {návrat createSpecification ((LogicalNode) uzol); } if (node ​​instanceof ComparisonNode) {return createSpecification ((ComparisonNode) node); } return null; } verejná špecifikácia createSpecification (LogicalNode logicalNode) {List specs = logicalNode.getChildren () .stream () .map (node ​​-> createSpecification (node)) .filter (Objects :: nonNull) .collect (Collectors.toList ()); Výsledok špecifikácie = specs.get (0); if (logicalNode.getOperator () == LogicalOperator.AND) {for (int i = 1; i <specs.size (); i ++) {result = Specification.where (result) .and (specs.get (i)) ; }} else if (logicalNode.getOperator () == LogicalOperator.OR) {for (int i = 1; i <specs.size (); i ++) {result = Specification.where (result) .or (specs.get ( i)); }} vrátiť výsledok; } public Specification createSpecification (ComparisonNode comparisonNode) {Specification result = Specification.where (new GenericRsqlSpecification (comparisonNode.getSelector (), comparisonNode.getOperator (), comparisonNode.getArguments ())); návratový výsledok; }}

Všimnite si, ako:

  • LogickýNód je A/ALEBOUzol a má viac detí
  • ComparisonNode nemá deti a drží Selektor, operátor a argumenty

Napríklad pre dopyt „meno == john" - máme:

  1. Selektor: "názov"
  2. Prevádzkovateľ: “==”
  3. Argumenty: [john]

4. Vytvorte vlastné Špecifikácia

Pri konštrukcii dotazu sme použili a Špecifikácia:

verejná trieda GenericRsqlSpecification implementuje Specification {private String property; súkromný operátor ComparisonOperator; argumenty súkromného zoznamu; @Override public Predicate toPredicate (root root, CriteriaQuery query, CriteriaBuilder builder) {List args = castArguments (root); Argument objektu = args.get (0); switch (RsqlSearchOperation.getSimpleOperator (operator)) {case EQUAL: {if (argument instanceof String) {return builder.like (root.get (property), argument.toString (). replace ('*', '%')) ; } else if (argument == null) {return builder.isNull (root.get (property)); } else {návrat builder.equal (root.get (vlastnosť), argument); }} prípad NOT_EQUAL: {if (argument instanceof String) {return builder.notLike (root. get (property), argument.toString (). replace ('*', '%')); } else if (argument == null) {return builder.isNotNull (root.get (property)); } else {návrat builder.notEqual (root.get (vlastnosť), argument); }} prípad GREATER_THAN: {návrat builder.greaterThan (root. get (vlastnosť), argument.toString ()); } prípad GREATER_THAN_OR_EQUAL: {návrat builder.greaterThanOrEqualTo (root. get (vlastnosť), argument.toString ()); } prípad LESS_THAN: {návrat builder.lessThan (root. get (vlastnosť), argument.toString ()); } prípad LESS_THAN_OR_EQUAL: {návrat builder.lessThanOrEqualTo (root. get (vlastnosť), argument.toString ()); } prípad IN: návrat root.get (vlastnosť) .in (args); prípad NOT_IN: návrat builder.not (root.get (vlastnosť) .in (args)); } return null; } private List castArgumenty (konečný koreňový koreň) {Typ triedy = root.get (vlastnosť) .getJavaType (); Zoznam args = arguments.stream (). Mapa (arg -> {if (type.equals (Integer.class)) {návrat Integer.parseInt (arg);} else if (type.equals (Long.class)) {návrat Long.parseLong (arg);} else {return arg;}}). Collect (Collectors.toList ()); návrat args; } // štandardný konštruktor, getter, setter}

Všimnite si, ako špecifikácia používa všeobecné informácie a nie je viazaná na žiadny konkrétny subjekt (napríklad Používateľa).

Ďalej - tu je náš enum “RsqlSearchOperation ktorý obsahuje predvolené operátory rsql-parser:

verejné enum RsqlSearchOperation {EQUAL (RSQLOperators.EQUAL), NOT_EQUAL (RSQLOperators.NOT_EQUAL), greater_than (RSQLOperators.GREATER_THAN), GREATER_THAN_OR_EQUAL (RSQLOperators.GREATER_THAN_OR_EQUAL), less_than (RSQLOperators.LESS_THAN), LESS_THAN_OR_EQUAL (RSQLOperators.LESS_THAN_OR_EQUAL), V (RSQLOperators. IN), NOT_IN (RSQLOperators.NOT_IN); súkromný operátor ComparisonOperator; private RsqlSearchOperation (ComparisonOperator operator) {this.operator = operator; } verejný statický RsqlSearchOperation getSimpleOperator (operátor ComparisonOperator) {pre (operácia RsqlSearchOperation: values ​​()) {if (operation.getOperator () == operátor) {návratová operácia; }} return null; }}

5. Vyskúšajte vyhľadávacie dotazy

Začnime teraz testovať naše nové a flexibilné operácie prostredníctvom niekoľkých scenárov z reálneho sveta:

Najprv - inicializujme údaje:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (classes = {PersistenceConfig.class}) @Transactional @TransactionConfiguration verejná trieda RsqlTest {@Autowired súkromné ​​úložisko UserRepository; súkromný používateľ userJohn; súkromný používateľ userTom; @ Pred public void init () {userJohn = nový používateľ (); userJohn.setFirstName ("john"); userJohn.setLastName ("laň"); userJohn.setEmail ("[chránený e-mailom]"); userJohn.setAge (22); repository.save (userJohn); userTom = nový užívateľ (); userTom.setFirstName ("tom"); userTom.setLastName ("laň"); userTom.setEmail ("[chránený e-mailom]"); userTom.setAge (26); repository.save (userTom); }}

Teraz otestujme rôzne operácie:

5.1. Testovať rovnosť

V nasledujúcom príklade - budeme vyhľadávať používateľov podľa ich najprv a priezvisko:

@Test public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect () {Node rootNode = new RSQLParser (). Parse ("firstName == john; lastName == doe"); Špecifikácia spec = rootNode.accept (nový CustomRsqlVisitor ()); Zoznam výsledkov = repository.findAll (spec); assertThat (userJohn, isIn (výsledky)); assertThat (userTom, nie (isIn (výsledky))); }

5.2. Testujte negáciu

Ďalej poďme hľadať používateľov, ktorí sú podľa nich krstné meno nie „john“:

@Test public void givenFirstNameInverse_whenGettingListOfUsers_thenCorrect () {Node rootNode = new RSQLParser (). Parse ("firstName! = John"); Špecifikácia spec = rootNode.accept (nový CustomRsqlVisitor ()); Zoznam výsledkov = repository.findAll (spec); assertThat (userTom, isIn (výsledky)); assertThat (userJohn, nie (isIn (results))); }

5.3. Vyskúšajte väčšie ako

Ďalej - budeme hľadať používateľov s Vek väčší než "25”:

@Test public void givenMinAge_whenGettingListOfUsers_thenCorrect () {Node rootNode = new RSQLParser (). Parse ("age> 25"); Špecifikácia spec = rootNode.accept (nový CustomRsqlVisitor ()); Zoznam výsledkov = repository.findAll (spec); assertThat (userTom, isIn (výsledky)); assertThat (userJohn, nie (isIn (results))); }

5.4. Vyskúšajte ako

Ďalej - budeme hľadať používateľov s ich krstné meno začínajúce „jo”:

@Test public void givenFirstNamePrefix_whenGettingListOfUsers_thenCorrect () {Node rootNode = new RSQLParser (). Parse ("firstName == jo *"); Špecifikácia spec = rootNode.accept (nový CustomRsqlVisitor ()); Zoznam výsledkov = repository.findAll (spec); assertThat (userJohn, isIn (výsledky)); assertThat (userTom, nie (isIn (výsledky))); }

5.5. Test IN

Ďalej - vyhľadáme ich používateľov krstné meno je "john“Alebo„zdvihák“:

@Test public void givenListOfFirstName_whenGettingListOfUsers_thenCorrect () {Uzol rootNode = nový RSQLParser (). Parse ("firstName = in = (john, jack)"); Špecifikácia spec = rootNode.accept (nový CustomRsqlVisitor ()); Zoznam výsledkov = repository.findAll (spec); assertThat (userJohn, isIn (výsledky)); assertThat (userTom, nie (isIn (výsledky))); }

6. UserController

Nakoniec - všetko spojíme s ovládačom:

@RequestMapping (method = RequestMethod.GET, value = "/ users") @ResponseBody public List findAllByRsql (@RequestParam (value = "search") String search) {Node rootNode = new RSQLParser (). Parse (search); Špecifikácia spec = rootNode.accept (nový CustomRsqlVisitor ()); vrátiť dao.findAll (spec); }

Tu je ukážka adresy URL:

// localhost: 8080 / users? search = firstName == jo *; vek <25

A odpoveď:

[{"id": 1, "firstName": "john", "lastName": "doe", "email": "[chránený e-mailom]," vek ": 24}]

7. Záver

Tento výukový program ilustroval, ako vytvoriť dotazovací / vyhľadávací jazyk pre rozhranie REST API bez toho, aby ste museli znova vymýšľať syntax a namiesto toho používať FIQL / RSQL.

The úplná implementácia tohto článku nájdete v projekte GitHub - jedná sa o projekt založený na Maven, takže by malo byť ľahké ho importovať a spustiť tak, ako je.

Ďalšie » ODPOČÍTAJTE Dotazovací jazyk s webovou podporou Querydsl « Predchádzajúci dotazovací jazyk REST - implementácia ALEBO prevádzka REST dole

Práve som oznámil nové Naučte sa jar kurz zameraný na základy jari 5 a Spring Boot 2:

>> SKONTROLUJTE KURZ Perzistencia dno

Práve som oznámil nové Naučte sa jar kurz zameraný na základy jari 5 a Spring Boot 2:

>> SKONTROLUJTE KURZ

$config[zx-auto] not found$config[zx-overlay] not found