diff --git a/README.md b/README.md index 3143ca7..97c9161 100644 --- a/README.md +++ b/README.md @@ -134,10 +134,14 @@ Request : `/cars?search=color!Red` 3. Using the greater than operator `>` Request : `/cars?search=creationyear>2017` +> Note: You can use the `>:` operator as well. + ![greater than operator example](./docs/images/greater-than-example.gif) 4. Using the less than operator `<` -Request : `/cars?search=price<100000` +Request : `/cars?search=price<100000` +> Note: You can use the `<:` operator as well. + ![less than operator example](./docs/images/less-than-example.gif) 5. Using the starts with operator `*` diff --git a/src/main/antlr4/Query.g4 b/src/main/antlr4/Query.g4 index 2a2a373..0bf8374 100644 --- a/src/main/antlr4/Query.g4 +++ b/src/main/antlr4/Query.g4 @@ -33,7 +33,9 @@ value op : EQ | GT + | GTE | LT + | LTE | NOT_EQ ; @@ -137,11 +139,17 @@ GT : '>' ; +GTE + : '>:' + ; LT : '<' ; +LTE + : '<:' + ; EQ : ':' diff --git a/src/main/kotlin/com/sipios/springsearch/SearchCriteria.kt b/src/main/kotlin/com/sipios/springsearch/SearchCriteria.kt index e87ba7e..adcc69c 100644 --- a/src/main/kotlin/com/sipios/springsearch/SearchCriteria.kt +++ b/src/main/kotlin/com/sipios/springsearch/SearchCriteria.kt @@ -5,7 +5,7 @@ class SearchCriteria // Change EQUALS into ENDS_WITH, CONTAINS, STARTS_WITH base var operation: SearchOperation? init { - var op = SearchOperation.getSimpleOperation(operation[0]) + var op = SearchOperation.getSimpleOperation(operation) if (op != null) { // Change EQUALS into ENDS_WITH, CONTAINS, STARTS_WITH based on the presence of * in the value val startsWithAsterisk = prefix != null && prefix.contains(SearchOperation.ZERO_OR_MORE_REGEX) diff --git a/src/main/kotlin/com/sipios/springsearch/SearchOperation.kt b/src/main/kotlin/com/sipios/springsearch/SearchOperation.kt index d6a1a04..f8f024a 100644 --- a/src/main/kotlin/com/sipios/springsearch/SearchOperation.kt +++ b/src/main/kotlin/com/sipios/springsearch/SearchOperation.kt @@ -1,10 +1,10 @@ package com.sipios.springsearch enum class SearchOperation { - EQUALS, NOT_EQUALS, GREATER_THAN, LESS_THAN, STARTS_WITH, ENDS_WITH, CONTAINS, DOESNT_START_WITH, DOESNT_END_WITH, DOESNT_CONTAIN; + EQUALS, NOT_EQUALS, GREATER_THAN, LESS_THAN, STARTS_WITH, ENDS_WITH, CONTAINS, DOESNT_START_WITH, DOESNT_END_WITH, DOESNT_CONTAIN, GREATER_THAN_EQUALS, LESS_THAN_EQUALS; companion object { - val SIMPLE_OPERATION_SET = arrayOf(":", "!", ">", "<", "~") + val SIMPLE_OPERATION_SET = arrayOf(":", "!", ">", "<", "~", ">:", "<:") val ZERO_OR_MORE_REGEX = "*" val OR_OPERATOR = "OR" val AND_OPERATOR = "AND" @@ -17,12 +17,14 @@ enum class SearchOperation { * @param input operation as string * @return The matching operation */ - fun getSimpleOperation(input: Char): SearchOperation? { + fun getSimpleOperation(input: String): SearchOperation? { return when (input) { - ':' -> EQUALS - '!' -> NOT_EQUALS - '>' -> GREATER_THAN - '<' -> LESS_THAN + ":" -> EQUALS + "!" -> NOT_EQUALS + ">" -> GREATER_THAN + "<" -> LESS_THAN + ">:" -> GREATER_THAN_EQUALS + "<:" -> LESS_THAN_EQUALS else -> null } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/DateStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/DateStrategy.kt index d240e70..61f5c69 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/DateStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/DateStrategy.kt @@ -22,6 +22,8 @@ class DateStrategy : ParsingStrategy { return when (ops) { SearchOperation.GREATER_THAN -> builder.greaterThan(path[fieldName], value as Date) SearchOperation.LESS_THAN -> builder.lessThan(path[fieldName], value as Date) + SearchOperation.GREATER_THAN_EQUALS -> builder.greaterThanOrEqualTo(path[fieldName], value as Date) + SearchOperation.LESS_THAN_EQUALS -> builder.lessThanOrEqualTo(path[fieldName], value as Date) else -> super.buildPredicate(builder, path, fieldName, ops, value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/DoubleStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/DoubleStrategy.kt index 135dfb3..7b1ccc0 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/DoubleStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/DoubleStrategy.kt @@ -17,6 +17,8 @@ class DoubleStrategy : ParsingStrategy { return when (ops) { SearchOperation.GREATER_THAN -> builder.greaterThan(path[fieldName], value as Double) SearchOperation.LESS_THAN -> builder.lessThan(path[fieldName], value as Double) + SearchOperation.GREATER_THAN_EQUALS -> builder.greaterThanOrEqualTo(path[fieldName], value as Double) + SearchOperation.LESS_THAN_EQUALS -> builder.lessThanOrEqualTo(path[fieldName], value as Double) else -> super.buildPredicate(builder, path, fieldName, ops, value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/DurationStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/DurationStrategy.kt index c35a847..a722fe2 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/DurationStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/DurationStrategy.kt @@ -18,6 +18,8 @@ class DurationStrategy : ParsingStrategy { return when (ops) { SearchOperation.GREATER_THAN -> builder.greaterThan(path[fieldName], value as Duration) SearchOperation.LESS_THAN -> builder.lessThan(path[fieldName], value as Duration) + SearchOperation.GREATER_THAN_EQUALS -> builder.greaterThanOrEqualTo(path[fieldName], value as Duration) + SearchOperation.LESS_THAN_EQUALS -> builder.lessThanOrEqualTo(path[fieldName], value as Duration) else -> super.buildPredicate(builder, path, fieldName, ops, value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/FloatStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/FloatStrategy.kt index 65e7525..ea1d1e6 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/FloatStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/FloatStrategy.kt @@ -17,6 +17,8 @@ class FloatStrategy : ParsingStrategy { return when (ops) { SearchOperation.GREATER_THAN -> builder.greaterThan(path[fieldName], value as Float) SearchOperation.LESS_THAN -> builder.lessThan(path[fieldName], value as Float) + SearchOperation.GREATER_THAN_EQUALS -> builder.greaterThanOrEqualTo(path[fieldName], value as Float) + SearchOperation.LESS_THAN_EQUALS -> builder.lessThanOrEqualTo(path[fieldName], value as Float) else -> super.buildPredicate(builder, path, fieldName, ops, value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/InstantStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/InstantStrategy.kt index 95aff1f..66294cd 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/InstantStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/InstantStrategy.kt @@ -18,6 +18,8 @@ class InstantStrategy : ParsingStrategy { return when (ops) { SearchOperation.GREATER_THAN -> builder.greaterThan(path[fieldName], value as Instant) SearchOperation.LESS_THAN -> builder.lessThan(path[fieldName], value as Instant) + SearchOperation.GREATER_THAN_EQUALS -> builder.greaterThanOrEqualTo(path[fieldName], value as Instant) + SearchOperation.LESS_THAN_EQUALS -> builder.lessThanOrEqualTo(path[fieldName], value as Instant) else -> super.buildPredicate(builder, path, fieldName, ops, value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/IntStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/IntStrategy.kt index fc8ceb1..21f3863 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/IntStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/IntStrategy.kt @@ -17,6 +17,8 @@ class IntStrategy : ParsingStrategy { return when (ops) { SearchOperation.GREATER_THAN -> builder.greaterThan(path[fieldName], value as Int) SearchOperation.LESS_THAN -> builder.lessThan(path[fieldName], value as Int) + SearchOperation.GREATER_THAN_EQUALS -> builder.greaterThanOrEqualTo(path[fieldName], value as Int) + SearchOperation.LESS_THAN_EQUALS -> builder.lessThanOrEqualTo(path[fieldName], value as Int) else -> super.buildPredicate(builder, path, fieldName, ops, value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateStrategy.kt index 57e8ae2..2b435f6 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateStrategy.kt @@ -18,6 +18,8 @@ class LocalDateStrategy : ParsingStrategy { return when (ops) { SearchOperation.GREATER_THAN -> builder.greaterThan(path[fieldName], value as LocalDate) SearchOperation.LESS_THAN -> builder.lessThan(path[fieldName], value as LocalDate) + SearchOperation.GREATER_THAN_EQUALS -> builder.greaterThanOrEqualTo(path[fieldName], value as LocalDate) + SearchOperation.LESS_THAN_EQUALS -> builder.lessThanOrEqualTo(path[fieldName], value as LocalDate) else -> super.buildPredicate(builder, path, fieldName, ops, value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateTimeStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateTimeStrategy.kt index 18e691f..763dae5 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateTimeStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateTimeStrategy.kt @@ -18,6 +18,8 @@ class LocalDateTimeStrategy : ParsingStrategy { return when (ops) { SearchOperation.GREATER_THAN -> builder.greaterThan(path[fieldName], value as LocalDateTime) SearchOperation.LESS_THAN -> builder.lessThan(path[fieldName], value as LocalDateTime) + SearchOperation.GREATER_THAN_EQUALS -> builder.greaterThanOrEqualTo(path[fieldName], value as LocalDateTime) + SearchOperation.LESS_THAN_EQUALS -> builder.lessThanOrEqualTo(path[fieldName], value as LocalDateTime) else -> super.buildPredicate(builder, path, fieldName, ops, value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/LocalTimeStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/LocalTimeStrategy.kt index 998c8de..7682689 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/LocalTimeStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/LocalTimeStrategy.kt @@ -18,6 +18,8 @@ class LocalTimeStrategy : ParsingStrategy { return when (ops) { SearchOperation.GREATER_THAN -> builder.greaterThan(path[fieldName], value as LocalTime) SearchOperation.LESS_THAN -> builder.lessThan(path[fieldName], value as LocalTime) + SearchOperation.GREATER_THAN_EQUALS -> builder.greaterThanOrEqualTo(path[fieldName], value as LocalTime) + SearchOperation.LESS_THAN_EQUALS -> builder.lessThanOrEqualTo(path[fieldName], value as LocalTime) else -> super.buildPredicate(builder, path, fieldName, ops, value) } } diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index 66eb2e6..492531b 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -592,6 +592,44 @@ class SpringSearchApplicationTest { Assertions.assertEquals(0, specificationUsers.size) } + @Test + fun canGetUsersAfterEqualDate() { + val sdf = StdDateFormat() + userRepository.save(Users(createdAt = sdf.parse("2019-01-01"))) + userRepository.save(Users(createdAt = sdf.parse("2019-01-03"))) + + var specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", true) + ).withSearch("createdAt>:'2019-01-01'").build() + var specificationUsers = userRepository.findAll(specification) + Assertions.assertEquals(2, specificationUsers.size) + + specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", true) + ).withSearch("createdAt>:'2019-01-04'").build() + specificationUsers = userRepository.findAll(specification) + Assertions.assertEquals(0, specificationUsers.size) + } + + @Test + fun canGetUsersEarlierEqualDate() { + val sdf = StdDateFormat() + userRepository.save(Users(createdAt = sdf.parse("2019-01-01"))) + userRepository.save(Users(createdAt = sdf.parse("2019-01-03"))) + + var specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", true) + ).withSearch("createdAt<:'2019-01-01'").build() + var specificationUsers = userRepository.findAll(specification) + Assertions.assertEquals(1, specificationUsers.size) + + specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", true) + ).withSearch("createdAt<:'2019-01-03'").build() + specificationUsers = userRepository.findAll(specification) + Assertions.assertEquals(2, specificationUsers.size) + } + @Test fun canGetUsersAtPreciseDate() { val sdf = StdDateFormat() @@ -742,7 +780,7 @@ class SpringSearchApplicationTest { } @Test - fun canGetUsersWithUpdatedInstantAtGreaterSearch() { + fun canGetUsersWithUpdateInstantAtGreaterSearch() { userRepository.save( Users( userFirstName = "HamidReza", @@ -760,10 +798,10 @@ class SpringSearchApplicationTest { } @Test - fun canGetUsersWithUpdatedInstantAtLessSearch() { + fun canGetUsersWithUpdateInstantAtGreaterThanEqualSearch() { userRepository.save( Users( - userFirstName = "HamidReza", + userFirstName = "john", updatedInstantAt = Instant.parse("2020-01-10T10:15:30Z") ) ) @@ -771,17 +809,17 @@ class SpringSearchApplicationTest { val specification = SpecificationsBuilder( SearchSpec::class.constructors.first().call("", false) - ).withSearch("updatedInstantAt<'2020-01-11T10:17:30Z'").build() + ).withSearch("updatedInstantAt>:'2020-01-11T09:20:30Z'").build() val robotUsers = userRepository.findAll(specification) Assertions.assertEquals(1, robotUsers.size) - Assertions.assertEquals("HamidReza", robotUsers[0].userFirstName) + Assertions.assertEquals("robot", robotUsers[0].userFirstName) } @Test - fun canGetUsersWithUpdatedInstantAtEqualSearch() { + fun canGetUsersWithUpdateInstantAtLessThanEqualSearch() { userRepository.save( Users( - userFirstName = "HamidReza", + userFirstName = "john", updatedInstantAt = Instant.parse("2020-01-10T10:15:30Z") ) ) @@ -789,10 +827,10 @@ class SpringSearchApplicationTest { val specification = SpecificationsBuilder( SearchSpec::class.constructors.first().call("", false) - ).withSearch("updatedInstantAt:'2020-01-10T10:15:30Z'").build() + ).withSearch("updatedInstantAt<:'2020-01-11T09:20:30Z'").build() val robotUsers = userRepository.findAll(specification) Assertions.assertEquals(1, robotUsers.size) - Assertions.assertEquals("HamidReza", robotUsers[0].userFirstName) + Assertions.assertEquals("john", robotUsers[0].userFirstName) } @Test @@ -834,6 +872,32 @@ class SpringSearchApplicationTest { Assertions.assertEquals("HamidReza", hamidrezaUsers[0].userFirstName) } + @Test + fun canGetUsersWithUpdatedAtGreaterThanEqualSearch() { + userRepository.save(Users(userFirstName = "john", updatedAt = LocalDateTime.parse("2020-01-10T10:15:30"))) + userRepository.save(Users(userFirstName = "robot", updatedAt = LocalDateTime.parse("2020-01-11T10:20:30"))) + userRepository.save(Users(userFirstName = "robot2", updatedAt = LocalDateTime.parse("2020-01-12T10:20:30"))) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("updatedAt>:'2020-01-11T10:20:30'").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(2, users.size) + Assertions.assertFalse(users.any { user -> user.userFirstName == "john" }) + } + + @Test + fun canGetUsersWithUpdatedAtLessThanEqualSearch() { + userRepository.save(Users(userFirstName = "john", updatedAt = LocalDateTime.parse("2020-01-10T10:15:30"))) + userRepository.save(Users(userFirstName = "robot", updatedAt = LocalDateTime.parse("2020-01-11T10:20:30"))) + userRepository.save(Users(userFirstName = "robot2", updatedAt = LocalDateTime.parse("2020-01-12T10:20:30"))) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("updatedAt<:'2020-01-11T10:20:30'").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(2, users.size) + Assertions.assertFalse(users.any { user -> user.userFirstName == "robot2" }) + } + @Test fun canGetUsersWithUpdatedDateAtGreaterSearch() { userRepository.save(Users(userFirstName = "HamidReza", updatedDateAt = LocalDate.parse("2020-01-10"))) @@ -886,6 +950,34 @@ class SpringSearchApplicationTest { Assertions.assertEquals("HamidReza", hamidrezaUsers[0].userFirstName) } + @Test + fun canGetUsersWithUpdatedDateAtLessThanEqualSearch() { + userRepository.save(Users(userFirstName = "john", updatedDateAt = LocalDate.parse("2020-01-10"))) + userRepository.save(Users(userFirstName = "robot", updatedDateAt = LocalDate.parse("2020-01-11"))) + userRepository.save(Users(userFirstName = "robot2", updatedDateAt = LocalDate.parse("2020-01-12"))) + + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("updatedDateAt<:'2020-01-11'").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(2, users.size) + Assertions.assertFalse(users.any { user -> user.userFirstName == "robot2" }) + } + + @Test + fun canGetUsersWithUpdatedDateAtGreaterThanEqualSearch() { + userRepository.save(Users(userFirstName = "john", updatedDateAt = LocalDate.parse("2020-01-10"))) + userRepository.save(Users(userFirstName = "robot", updatedDateAt = LocalDate.parse("2020-01-11"))) + userRepository.save(Users(userFirstName = "robot2", updatedDateAt = LocalDate.parse("2020-01-12"))) + + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("updatedDateAt>:'2020-01-11'").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(2, users.size) + Assertions.assertFalse(users.any { user -> user.userFirstName == "john" }) + } + @Test fun canGetUsersWithUpdatedTimeAtGreaterSearch() { userRepository.save(Users(userFirstName = "HamidReza", updatedTimeAt = LocalTime.parse("10:15:30"))) @@ -925,6 +1017,34 @@ class SpringSearchApplicationTest { Assertions.assertEquals("HamidReza", hamidrezaUsers[0].userFirstName) } + @Test + fun canGetUsersWithUpdatedTimeAtLessThanEqualSearch() { + userRepository.save(Users(userFirstName = "john", updatedTimeAt = LocalTime.parse("10:15:30"))) + userRepository.save(Users(userFirstName = "robot", updatedTimeAt = LocalTime.parse("10:20:30"))) + userRepository.save(Users(userFirstName = "robot2", updatedTimeAt = LocalTime.parse("10:25:30"))) + + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("updatedTimeAt<:'10:20:30'").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(2, users.size) + Assertions.assertFalse(users.any { user -> user.userFirstName == "robot2" }) + } + + @Test + fun canGetUsersWithUpdatedTimeAtGreaterThanEqualSearch() { + userRepository.save(Users(userFirstName = "john", updatedTimeAt = LocalTime.parse("10:15:30"))) + userRepository.save(Users(userFirstName = "robot", updatedTimeAt = LocalTime.parse("10:20:30"))) + userRepository.save(Users(userFirstName = "robot2", updatedTimeAt = LocalTime.parse("10:25:30"))) + + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("updatedTimeAt>:'10:20:30'").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(2, users.size) + Assertions.assertFalse(users.any { user -> user.userFirstName == "john" }) + } + @Test fun canGetUsersWithUpdatedTimeAtNotEqualSearch() { userRepository.save(Users(userFirstName = "HamidReza", updatedTimeAt = LocalTime.parse("10:15:30"))) @@ -1015,4 +1135,42 @@ class SpringSearchApplicationTest { Assertions.assertEquals(1, robotUsers.size) Assertions.assertEquals(user2UUID, robotUsers[0].uuid) } + + @Test + fun canGetUsersWithNumberOfChildrenLessOrEqualSearch() { + userRepository.save(Users(userFirstName = "john", userChildrenNumber = 2)) + userRepository.save(Users(userFirstName = "jane", userChildrenNumber = 3)) + userRepository.save(Users(userFirstName = "joe", userChildrenNumber = 4)) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("userChildrenNumber<:2").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(1, users.size) + Assertions.assertEquals("john", users[0].userFirstName) + } + + @Test + fun canGetUsersWithNumberOfChildrenGreaterOrEqualSearch() { + userRepository.save(Users(userFirstName = "john", userChildrenNumber = 2)) + userRepository.save(Users(userFirstName = "jane", userChildrenNumber = 3)) + userRepository.save(Users(userFirstName = "joe", userChildrenNumber = 4)) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("userChildrenNumber>:3").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(2, users.size) + } + + @Test + fun canGetUsersWithNumberOfChildrenLessSearch() { + userRepository.save(Users(userFirstName = "john", userChildrenNumber = 2)) + userRepository.save(Users(userFirstName = "jane", userChildrenNumber = 3)) + userRepository.save(Users(userFirstName = "joe", userChildrenNumber = 4)) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("userChildrenNumber<3").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(1, users.size) + Assertions.assertEquals("john", users[0].userFirstName) + } }