Spring Boot で JPA を使用して高度な動的クエリを構築および実行するためのライブラリ。
Map<String, String>
経由、クエリ パラメーターを使用した GET エンドポイントをサポートします。jpa-search-helperを通じて、コントローラー* は次のようなリクエストを受信できるようになります。
モード1 :
curl -X GET
' https://myexampledomain.com/persons?
firstName=Biagio
&lastName_startsWith=Toz
&birthDate_gte=19910101
&country_in=IT,FR,DE
&address_eq=Via Roma 1,Via Milano/,1,20 West/,34th Street
&company.name_in=Microsoft,Apple,Google
&company.employees_between=500,5000 '
モード2 :
curl -X POST -H " Content-type: application/json " -d ' {
"filter" : {
"operator": "and", // the first filter must contain a root operator: AND, OR or NOT
"filters" : [
{
"operator": "eq",
"key": "firstName",
"value": "Biagio"
},
{
"operator": "or",
"filters": [
{
"operator": "startsWith",
"key": "lastName",
"value": "Toz",
"options": {
"ignoreCase": true
}
},
{
"operator": "endsWith",
"key": "lastName",
"value": "ZZI",
"options": {
"ignoreCase": true,
"trim" : true
}
}
]
},
{
"operator": "in",
"key": "company.name",
"values": ["Microsoft", "Apple", "Google"]
},
{
"operator": "or",
"filters": [
{
"operator": "gte",
"key": "birthDate",
"value": "19910101"
},
{
"operator": "lte",
"key": "birthDate",
"value": "20010101"
}
]
},
{
"operator": "between",
"key" : "company.employees",
"values": [500, 5000],
"options": {
"negate": true
}
}
]
},
"options": {
"pageSize": 10,
"pageOffset": 0,
"sortKey": "birthDate",
"sortDesc": false
}
} ' ' https://myexampledomain.com/persons '
..どうやってやるの?このリードミーを読んでください!
*注意: このライブラリはコントローラー/HTTP エンドポイントを公開せず、クエリを構築して実行するリポジトリのみを提供します。
JPA検索ヘルパー | スプリングブーツ | ジャワ |
---|---|---|
[v0.0.1~v2.1.1] | 3.2.x | [17~23] |
[v3.0.0~v3.2.2] | 3.3.x | [17~23] |
[v3.3.0 - 最新] | 3.4.x | [17~23] |
<dependency>
<groupId>app.tozzi</groupId>
<artifactId>jpa-search-helper</artifactId>
<version>3.3.0</version>
</dependency>
implementation 'app.tozzi:jpa-search-helper:3.3.0
@Searchable
アノテーションまず、検索に使用できるようにするドメイン モデル、または JPA エンティティ内のフィールドに@Searchable
アノテーションを適用します。他のオブジェクト内で検索可能にしたいフィールドがある場合は、それらに@NestedSearchable
の注釈を付けます。
@ Data
public class Person {
@ Searchable
private String firstName ;
@ Searchable
private String lastName ;
@ Searchable ( entityFieldKey = "dateOfBirth" )
private Date birthDate ;
@ Searchable
private String country ;
@ Searchable
private String fillerOne ;
@ Searchable
private String fillerTwo ;
@ NestedSearchable
private Company company ;
@ Data
public static class Company {
@ Searchable ( entityFieldKey = "companyEntity.name" )
private String name ;
@ Searchable ( entityFieldKey = "companyEntity.employeesCount" )
private int employees ;
}
}
注釈を使用すると、以下を指定できます。
コアプロパティ:
entityFieldKey
: エンティティ Bean で定義されたフィールドの名前 (エンティティ Bean でアノテーションを使用する場合は指定できません)。指定しない場合、キーはフィールド名になります。
targetType
: エンティティごとの管理対象オブジェクトのタイプ。指定されていない場合、ライブラリはフィールド タイプに基づいてそれを取得しようとします (例: ターゲット タイプ定義のない整数フィールドはINTEGER
になります)。管理対象と互換性のある型がない場合は文字列として管理されます。管理対象タイプ:
STRING
、 INTEGER
、 DOUBLE
、 FLOAT
、 LONG
、 BIGDECIMAL
、 BOOLEAN
、 DATE
、 LOCALDATE
、 LOCALDATETIME
、 LOCALTIME
、 OFFSETDATETIME
、 OFFSETTIME
、 ZONEDDATETIME
。検証プロパティ:
datePattern
: DATE
、 LOCALDATE
、 LOCALDATETIME
、 LOCALTIME
、 OFFSETDATETIME
、 OFFSETTIME
、 ZONEDDATETIME
ターゲット タイプのみ。使用する日付パターンを定義します。maxSize, minSize
: 値の最大/最小長。maxDigits, minDigits
: 数値型のみ。最大/最小桁数。regexPattern
: 正規表現パターン。decimalFormat
: 10 進数値タイプのみ。デフォルト#.##
他の:
sortable
: false の場合、フィールドは検索には使用できますが、並べ替えには使用できません。デフォルト: true。trim
: トリムを適用します。tags
: ドメイン モデル フィールドが複数のエンティティ フィールドに対応できる場合に便利です (例はさらに下にあります)。allowedFilters
: 排他的に許可されるフィルター。notAllowedFilters
: 許可されていないフィルター。likeFilters
: 同様のフィルターが許可されます ( contains 、 startsWith 、 endsWith )。デフォルト: true。例を続けると、エンティティ クラスは次のようになります。
@ Entity
@ Data
public class PersonEntity {
@ Id
private Long id ;
@ Column ( name = "FIRST_NAME" )
private String firstName ;
@ Column ( name = "LAST_NAME" )
private String lastName ;
@ Column ( name = "BIRTH_DATE" )
private Date dateOfBirth ;
@ Column ( name = "COUNTRY" )
private String country ;
@ Column ( name = "FIL_ONE" )
private String fillerOne ;
@ Column ( name = "FIL_TWO" )
private String fillerTwo ;
@ OneToOne
private CompanyEntity companyEntity ;
}
@ Entity
@ Data
public class CompanyEntity {
@ Id
private Long id ;
@ Column ( name = "NAME" )
private String name ;
@ Column ( name = "COUNT" )
private Integer employeesCount ;
}
JPASearchRepository
インターフェースSpring JPA リポジトリはJPASearchRepository<YourEntityClass>
を拡張する必要があります。
@ Repository
public interface PersonRepository extends JpaRepository < PersonEntity , Long >, JPASearchRepository < PersonEntity > {
}
マネージャー、サービス、またはリポジトリを使用したい場所ならどこでも:
モード 1 : マップ<filter_key#options, value>を定義します。
// ...
@ Autowired
private PersonRepository personRepository ;
public List < Person > advancedSearch () {
// Pure example, in real use case it is expected that these filters can be passed directly by the controller
Map < String , String > filters = new HashMap <>();
filters . put ( "firstName_eq" , "Biagio" );
filters . put ( "lastName_startsWith#i" , "Toz" ); // ignore case
filters . put ( "birthDate_gte" , "19910101" );
filters . put ( "country_in" , "IT,FR,DE" );
filters . put ( "company.name_eq#n" , "Bad Company" ); // negation
filters . put ( "company.employees_between" , "500,5000" );
filters . put ( "fillerOne_null#n" , "true" ); // not null
filters . put ( "fillerTwo_empty" , "true" ); // empty
// Without pagination
List < PersonEntity > fullSearch = personRepository . findAll ( filters , Person . class );
filters . put ( "birthDate_sort" : "ASC" ); // sorting key and sorting order
filters . put ( "_limit" , "10" ); // page size
filters . put ( "_offset" , "0" ); // page offset
// With pagination
Page < PersonEntity > sortedAndPaginatedSearch = personRepository . findAllWithPaginationAndSorting ( filters , Person . class );
// ...
}
// ...
モード 2 : マップの代わりに、ここに示されているJPASearchInput
簡単にするために JSON 形式で使用する必要があります。
{
"filter" : {
"operator" : " and " , // the first filter must contain a root operator: AND, OR or NOT
"filters" : [
{
"operator" : " eq " ,
"key" : " firstName " ,
"value" : " Biagio "
},
{
"operator" : " or " ,
"filters" : [
{
"operator" : " startsWith " ,
"key" : " lastName " ,
"value" : " Toz " ,
"options" : {
"ignoreCase" : true
}
},
{
"operator" : " endsWith " ,
"key" : " lastName " ,
"value" : " ZZI " ,
"options" : {
"ignoreCase" : true ,
"trim" : true
}
}
]
},
{
"operator" : " in " ,
"key" : " company.name " ,
"values" : [ " Microsoft " , " Apple " , " Google " ]
},
{
"operator" : " or " ,
"filters" : [
{
"operator" : " gte " ,
"key" : " birthDate " ,
"value" : " 19910101 "
},
{
"operator" : " lte " ,
"key" : " birthDate " ,
"value" : " 20010101 "
}
]
},
{
"operator" : " empty " ,
"key" : " fillerOne " ,
"options" : {
"negate" : true
}
},
{
"operator" : " between " ,
"key" : " company.employees " ,
"values" : [ 500 , 5000 ],
"options" : {
"negate" : true
}
}
]
},
"options" : {
"pageSize" : 10 ,
"pageOffset" : 0 ,
"sortKey" : " birthDate " ,
"sortDesc" : false
}
}
モード 2では、 AND
、 OR
、 NOT
を使用して複雑なフィルターを管理できます (後述)。
InvalidFieldException
を受け取ります。InvalidValueException
を受け取ります。JPASearchException
フィルター名 | ライブラリキー | サポートされているモード |
---|---|---|
そして | そして | 1、2 |
または | または | 2 |
ない | ない | 2 |
モード 1では、すべてのフィルターはAND
検索のみを構成します。
他の演算子OR
とNOT
を使用するには、モード 2 を使用する必要があります。
フィルター名 | ライブラリキー | SQL | サポートされているモード | 必要な値 |
---|---|---|---|---|
等しい | 等価 | sql_col = val | 1、2 | はい |
含まれています | 含まれています | sql_col LIKE '%val%' | 1、2 | はい |
で | で | sql_col IN (val1、val2、...、valN) | 1、2 | はい |
で始まる | で始まる | sql_col LIKE 'val%' | 1、2 | はい |
で終わる | で終わる | sql_col LIKE '%val' | 1、2 | はい |
より大きい | GT | SQL_col > val | 1、2 | はい |
以上 | ジーテ | sql_col >= val | 1、2 | はい |
未満 | それ | sql_col < val | 1、2 | はい |
以下 | LTE | sql_col <= val | 1、2 | はい |
間 | 間 | val1 と val2 の間の sql_col | 1、2 | はい |
ヌル | ヌル | sql_col が NULL です | 1、2 | いいえ |
空の | 空の | sql_collection_col が NULL です | 1、2 | いいえ |
モード1
オプションの説明 | ライブラリキー |
---|---|
大文字と小文字を無視する | #私 |
否定 | #n |
トリム | #t |
オプション キーをフィルターに追加する必要があります。例: ?firstName_eq#i=Biagioまたは?firstName_eq#i#n=Biagio
モード2
オプションの説明 | ライブラリキー (Java 属性) |
---|---|
大文字と小文字を無視する | 無視するケース |
否定 | 否定する |
トリム | トリム |
フィルターごとにoptions
定義できます
{
// ...
{
"operator" : " eq " ,
"key" : " firstName " ,
"value" : " Biagio " ,
"options" : {
"ignoreCase" : true ,
"trim" : false ,
"negate" : true
}
}
// ...
}
Java オブジェクト:
@ Data
public static class JPASearchFilterOptions {
private boolean ignoreCase ;
private boolean trim ;
private boolean negate ;
}
フィルター名 | 鍵 | 固定値 |
---|---|---|
制限(ページサイズ) | 限界 | |
オフセット(ページ番号) | オフセット | |
選別 | 選別 | 昇順、降順 |
モード 1 : 例: ?firstName_sort=DESC&_limit=10&_offset=0
モード 2 : 値ルートoptions
:
{
"filter" : {
// ...
},
"options" : {
"sortKey" : " firstName " ,
"sortDesc" : true ,
"pageSize" : 10 ,
"pageOffset" : 1
}
}
Java オブジェクト:
@ Data
public static class JPASearchOptions {
private String sortKey ;
private Boolean sortDesc = false ;
private Integer pageSize ;
private Integer pageOffset ;
private List < String > selections ;
}
,
: 例: ?myField_in=test1,test2 --> 検索する値: [" test1 ", " test2 "]/,
: 例: ?myField_in=test1,test2/,test3 --> 検索する値: [" test1 ", " test2,test3 "] @Projectable
アノテーションまず、選択可能にしたいドメイン モデル、または JPA エンティティのフィールドに@Projectable
アノテーションを適用します。他のオブジェクト内で選択可能にしたいフィールドがある場合は、それらに@NestedProjectable
の注釈を付けます。
@ Data
public class Person {
@ Searchable
private String firstName ;
@ Projectable
@ Searchable
private String lastName ;
@ Projectable ( entityFieldKey = "dateOfBirth" )
@ Searchable ( entityFieldKey = "dateOfBirth" )
private Date birthDate ;
@ Searchable
private String country ;
@ Searchable
private String fillerOne ;
@ Searchable
private String fillerTwo ;
@ NestedProjectable
@ NestedSearchable
private Company company ;
@ Data
public static class Company {
@ Searchable ( entityFieldKey = "companyEntity.name" )
private String name ;
@ Projectable ( entityFieldKey = "companyEntity.employeesCount" )
@ Searchable ( entityFieldKey = "companyEntity.employeesCount" )
private int employees ;
}
}
注釈を使用すると、以下を指定できます。
コアプロパティ:
entityFieldKey
: エンティティ Bean で定義されたフィールドの名前 (エンティティ Bean でアノテーションを使用する場合は指定できません)。指定しない場合、キーはフィールド名になります。JPASearchRepository
インターフェースSpring JPA リポジトリはJPAProjectionRepository<YourEntityClass>
を拡張する必要があります。
@ Repository
public interface PersonRepository extends JpaRepository < PersonEntity , Long >, JPASearchRepository < PersonEntity >, JPAProjectionRepository < PersonEntity > {
}
マネージャー、サービス、またはリポジトリを使用したい場所ならどこでも:
モード 1 : マップを定義します (またはモード 1 の検索に使用されるマップに追加します)。
,
// ...
@ Autowired
private PersonRepository personRepository ;
public List < Person > advancedSearch () {
// Pure example, in real use case it is expected that these filters can be passed directly by the controller
Map < String , String > filters = new HashMap <>();
filters . put ( "firstName_eq" , "Biagio" );
filters . put ( "lastName_startsWith#i" , "Toz" ); // ignore case
filters . put ( "birthDate_gte" , "19910101" );
filters . put ( "country_in" , "IT,FR,DE" );
filters . put ( "company.name_eq#n" , "Bad Company" ); // negation
filters . put ( "company.employees_between" , "500,5000" );
filters . put ( "fillerOne_null#n" , "true" ); // not null
filters . put ( "fillerTwo_empty" , "true" ); // empty
// Selections
filters . put ( "selections" , "lastName,birthDate,company.employees" );
// Without sorting
List < Map < String , Object >> result = personRepository . projection ( filters , Person . class , PersonEntity . class );
filters . put ( "birthDate_sort" : "ASC" ); // sorting key and sorting order
// With sorting
List < Map < String , Object >> sortedAndPaginatedSearch = personRepository . projectionWithSorting ( filters , Person . class , PersonEntity . class );
// ... convert the list of maps into your model
}
// ...
モード 2 : マップの代わりに、ここに示されているJPASearchInput
簡単にするために JSON 形式で使用する必要があります。
{
"filter" : {
"operator" : " and " , // the first filter must contain a root operator: AND, OR or NOT
"filters" : [
{
"operator" : " eq " ,
"key" : " firstName " ,
"value" : " Biagio "
},
{
"operator" : " or " ,
"filters" : [
{
"operator" : " startsWith " ,
"key" : " lastName " ,
"value" : " Toz " ,
"options" : {
"ignoreCase" : true
}
},
{
"operator" : " endsWith " ,
"key" : " lastName " ,
"value" : " ZZI " ,
"options" : {
"ignoreCase" : true ,
"trim" : true
}
}
]
},
{
"operator" : " in " ,
"key" : " company.name " ,
"values" : [ " Microsoft " , " Apple " , " Google " ]
},
{
"operator" : " or " ,
"filters" : [
{
"operator" : " gte " ,
"key" : " birthDate " ,
"value" : " 19910101 "
},
{
"operator" : " lte " ,
"key" : " birthDate " ,
"value" : " 20010101 "
}
]
},
{
"operator" : " empty " ,
"key" : " fillerOne " ,
"options" : {
"negate" : true
}
},
{
"operator" : " between " ,
"key" : " company.employees " ,
"values" : [ 500 , 5000 ],
"options" : {
"negate" : true
}
}
]
},
"options" : {
"pageSize" : 10 ,
"pageOffset" : 0 ,
"sortKey" : " birthDate " ,
"sortDesc" : false ,
"selections" : [
" lastName " ,
" birthDate " ,
" company.employees "
]
}
}
どちらのモードでも、プロジェクションは、マップ構造とキーがエンティティ構造を反映するList<Map<String, Object>> 結果を返します (明確にするためにtoJson(entityList) == toJson(mapList) )。
注1:
注意: デフォルトの射影では、すべての結合関係が LEFT JOIN として強制されます。この動作を望まない場合は、変更したいリレーションのみを変更できるリポジトリ メソッド (「Classic」サフィックスが付いたメソッド) の使用を選択してください。
注2:
プロジェクションでは、必要かどうかに関係なく、エンティティ (または関連エンティティ) の主キーを表すフィールドが常に抽出されます。
注3:
ページネーションはサポートされていません
InvalidFieldException
を受け取ります。JPASearchException
フェッチによる結合を強制して、Hibernate (または JPA フレームワーク) がエンティティで定義された関係に対して単一のクエリを実行できるようにすることができます。これはページネーションを使用しない場合にのみ可能です。
// ...
Map < String , JoinFetch > fetches = Map . of ( "companyEntity" , JoinFetch . LEFT );
personRepository . findAll ( filters , Person . class , fetches );
// ...
複数のエンティティの変換の結果であるドメイン モデルがある場合は、キーがドメイン モデル フィールドの名前を表し、値がドメイン モデルのフィールドの名前であるマップ (文字列、文字列) を明示的に指定することができます。検索するエンティティ:
// ...
Map < String , String > entityFieldMap = Map . of ( "company" , "companyEntity.name" );
// Without pagination
personRepository . findAll ( filters , Person . class , fetches , entityFieldMap );
// With pagination
personRepository . findAllWithPaginationAndSorting ( filters , Person . class , entityFieldMap );
// ...
もう 1 つの特殊なケースは、エンティティの複数の部分を表すためにドメイン モデル内でオブジェクトを繰り返すことができる場合です。検索の解決策:
@ Entity
public class CoupleEntity {
@ Id
private Long id ;
@ Column ( name = "p1_fn" )
private String p1FirstName ;
@ Column ( name = "p1_ln" )
private String p1LastName ;
@ Column ( name = "p2_fn" )
private String p2FirstName ;
@ Column ( name = "p2_ln" )
private String p2LastName ;
}
@ Data
public class Couple {
@ NestedSearchable
private Person p1 ;
@ NestedSearchable
private Person p2 ;
@ Data
public static class Person {
@ Searchable ( tags = {
@ Tag ( fieldKey = "p1.firstName" , entityFieldKey = "p1FirstName" ),
@ Tag ( fieldKey = "p2.firstName" , entityFieldKey = "p2FirstName" ),
})
private String firstName ;
@ Searchable ( tags = {
@ Tag ( fieldKey = "p1.lastName" , entityFieldKey = "p1LastName" ),
@ Tag ( fieldKey = "p2.lastName" , entityFieldKey = "p2LastName" ),
})
private String lastName ;
}
}
curl - request GET
- url ' https://www.myexampledomain.com/couples?
p1.firstName_iEq=Romeo
&p2.firstName_iEq=Giulietta '
注意: このライブラリはエンドポイントを公開しないため、コントローラーも公開しません。網羅的かつ完全なサンプル プロジェクトは、ここから入手できます。
コントローラ:
@ RestController
@ RequestMapping ( "/persons" )
public class PersonController {
@ Autowired
private PersonManager personManager ;
@ GetMapping ( produces = MediaType . APPLICATION_JSON_VALUE )
public List < Person > findPersons ( @ RequestParam Map < String , String > requestParams ) {
return personManager . find ( requestParams );
}
@ GetMapping ( path = "/projection" , produces = MediaType . APPLICATION_JSON_VALUE )
public List < Person > projection ( @ RequestParam Map < String , String > requestParams ) {
return personManager . projection ( requestParams );
}
}
サービス/マネージャー Bean:
@ Service
public class PersonManager {
@ Autowired
private PersonRepository personRepository ;
public List < Person > find ( Map < String , String > filters ) {
return personRepository . findAllWithPaginationAndSorting ( filters , Person . class ). stream (). map ( this :: toModel ). toList ();
}
public List < Person > projection ( Map < String , String > filters ) {
return personRepository . projection ( filters , Person . class , PersonEntity . class ). stream (). map ( this :: toModel ). toList ();
}
private static Person toModel ( PersonEntity personEntity ) {
// ...
}
private static Person toModel ( Map < String , Object > entityMap ) {
// ...
}
}
カール:
curl - X GET
' http://localhost:8080/persons?
firstName=Biagio
&lastName_startsWith=Toz
&birthDate_gte=19910101
&country_in=IT,FR,DE
&company.name_in=Microsoft,Apple
&company.employees_between=500,5000 '
または
curl - X GET
' http://localhost:8080/persons/projection?
firstName=Biagio
&lastName_startsWith=Toz
&birthDate_gte=19910101
&country_in=IT,FR,DE
&company.name_in=Microsoft,Apple
&company.employees_between=500,5000
&selections=firstName,birthDate '
コントローラ:
@ RestController
@ RequestMapping ( "/persons" )
@ Validated
public class PersonController {
@ Autowired
private PersonManager personManager ;
@ PostMapping ( produces = MediaType . APPLICATION_JSON_VALUE , consumes = MediaType . APPLICATION_JSON_VALUE )
public List < Person > findPersons ( @ Valid @ RequestBody JPASearchInput input ) {
return personManager . find ( input );
}
}
@ PostMapping ( path = "/projection" , produces = MediaType . APPLICATION_JSON_VALUE , consumes = MediaType . APPLICATION_JSON_VALUE )
public List < Person > projection ( @ Valid @ RequestBody JPASearchInput input ) {
return personManager . projection ( input );
}
}
サービス/マネージャー Bean:
@ Service
public class PersonManager {
@ Autowired
private PersonRepository personRepository ;
public List < Person > find ( JPASearchInput input ) {
return personRepository . findAllWithPaginationAndSorting ( input , Person . class ). stream (). map ( this :: toModel ). toList ();
}
public List < Person > find ( JPASearchInput input ) {
return personRepository . projection ( input , Person . class , PersonEntity . class ). stream (). map ( this :: toModel ). toList ();
}
private static Person toModel ( PersonEntity entity ) {
// ...
}
private static Person toModel ( Map < String , Object > entityMap ) {
// ...
}
}
カール:
curl -X POST -H " Content-type: application/json " -d ' {
"filter" : {
"operator": "and", // the first filter must contain a root operator: AND, OR or NOT
"filters" : [
{
"operator": "eq",
"key": "firstName",
"value": "Biagio"
},
{
"operator": "or",
"filters": [
{
"operator": "startsWith",
"key": "lastName",
"value": "Toz",
"options": {
"ignoreCase": true
}
},
{
"operator": "endsWith",
"key": "lastName",
"value": "ZZI",
"options": {
"ignoreCase": true,
"trim" : true
}
}
]
},
{
"operator": "in",
"key": "company.name",
"values": ["Microsoft", "Apple", "Google"]
},
{
"operator": "or",
"filters": [
{
"operator": "gte",
"key": "birthDate",
"value": "19910101"
},
{
"operator": "lte",
"key": "birthDate",
"value": "20010101"
}
]
},
{
"operator": "between",
"key" : "company.employees",
"values": [500, 5000],
"options": {
"negate": true
}
}
]
},
"options": {
"pageSize": 10,
"pageOffset": 0,
"sortKey": "birthDate",
"sortDesc": false
}
} ' ' http://localhost:8080/persons '
または
curl -X POST -H " Content-type: application/json " -d ' {
"filter" : {
"operator": "and", // the first filter must contain a root operator: AND, OR or NOT
"filters" : [
{
"operator": "eq",
"key": "firstName",
"value": "Biagio"
},
{
"operator": "or",
"filters": [
{
"operator": "startsWith",
"key": "lastName",
"value": "Toz",
"options": {
"ignoreCase": true
}
},
{
"operator": "endsWith",
"key": "lastName",
"value": "ZZI",
"options": {
"ignoreCase": true,
"trim" : true
}
}
]
},
{
"operator": "in",
"key": "company.name",
"values": ["Microsoft", "Apple", "Google"]
},
{
"operator": "or",
"filters": [
{
"operator": "gte",
"key": "birthDate",
"value": "19910101"
},
{
"operator": "lte",
"key": "birthDate",
"value": "20010101"
}
]
},
{
"operator": "between",
"key" : "company.employees",
"values": [500, 5000],
"options": {
"negate": true
}
}
]
},
"options": {
"sortKey": "birthDate",
"sortDesc": false,
"selections" : [
"birthDate",
"firstName",
"lastName"
]
}
} ' ' http://localhost:8080/persons/projection '