使用 Spring 数据 mongodb 的动态参数化查询
dynamic parametrized queries using Spring data mongodb
我正在寻找一种动态创建参数化查询的方法。本质上,我想将查询与应用程序分离并使它们可配置。
例如,我想创建一个带有参数的查询,例如
{ firstName: ?0 }
您可能知道,这在 Spring 数据 mongodb 中使用接口是完全可行的:
interface MyQuery {
@Query("{firstName: ?0}")
public Person getByFirstName(final String name);
}
但这会在编译时耦合我的查询,而我想在运行时更改它们。
但是我找不到动态使用此机制的方法。幕后使用的特定 class 是 StringBasedMongoQuery
,并使用大量反射实用程序来确定查询、参数、返回类型等。您可以使用 [=66 管理大部分内容=] 我认为,但是注释似乎是泡菜。
这让我觉得我选择了错误的方法来解决这个问题。
我自己只看到了几个选项:
- 我自己为 mongo 重新实现 Spring 的花式查询解析(看起来工作量很大)
- 自己实现穷人的查询参数替换。 (这不灵活,不会在空参数上产生正确的结果...)
我在这里很茫然,我似乎不能单独使用StringBasedMongoQuery
太糟糕了,它似乎与反射耦合。
更新:
我的建议在这里似乎毫无意义,因为我还注意到 Spring 数据 mongodb 的 StringBasedMongoQuery
不支持 'dynamic' 具有可为空参数的查询;事实上,这是非常合乎逻辑的,当参数为 null 时,很难从查询中删除部分内容,而且完全不可能做到正确。
以下说明了这个问题:https://jira.spring.io/browse/DATAJPA-209
所以我可能的选择似乎已经转移到:
- 我们进行不同的查询,使用的查询取决于可用的参数。这可能会给维护带来噩梦。
- 我们公开了一个 groovy 或其他 JVM 脚本,允许我们使用 Spring 或 mongodb 的一个可用的 Criteria-esque API Java 驱动程序,它将为我们构建查询。这样做的缺点是现在想要配置查询的人至少必须知道 Java。
- 我们在需要时以牺牲可读性为代价使我们的查询空值安全。
与我在此 post 中提出的建议相比,我找到了解决此问题的更好、更简单的方法。我会告诉你我将如何解决这个问题,以供将来其他人参考。
虽然 mongodb 没有查询参数之类的东西,但我们可以通过使用 mongodb 提供的一些其他相当独特的构造以及严格的结构来使事情变得容易得多。我们可以坚持这种结构,因为它涉及用户的动态可配置查询。
我们的结构很简单,如下。我们有一个基本的查询结构,如下所示:
{ $and: []}
我们定义了一个将始终被填充的基本查询。这可以像空 JS 对象一样简单。这样,我们的查询就变成了
{ $and: [{}] }
所以,这只是查询所有内容。现在我们可以为每个可以动态的条件定义一个子查询。如果满足条件,我们可以选择将此查询添加到 $and
中。由于我们坚持将所有内容放在一个和构造中,我们知道查询的外观,并且我们不需要非常花哨的参数替换来获得非常灵活的东西来轻松工作。
一个查询可以变成例如:
{ $and: [{}, {active: true}, {started: false}]}
我们可以随时添加那些小子查询以添加和启动。
解决此问题的另一种方法是使用示例对象...我正在利用 spring-data-mongodb 顺便说一句,所以不确定这是否适用于您。 ..
public interface PetRepository extends MongoRepository<Pet, String>{
}
...
@Service
public class PetService {
private Logger logger = LogManager.getLogger(PetService.class);
@Autowired
PetRepository petRepository;
public List<Pet> findPetsByExample(String name, String sex, String color, Integer age){
//Guard clauses omitted...
Example<Pet> example = Example.of(new Pet(name, sex, color, age));
return Lists.newArrayList(petRepository.findAll(example));
}
现在它正在做的是利用在 MongoRepository 中扩展的 QueryByExampleExecutor 类在引擎盖下将提供的示例 pet 与您的文档内容进行比较。这将只查看我们的示例 pet 上已提供值的字段,并且将在比较中忽略空值。您还可以包括自定义匹配器以执行进一步的过滤。这是一些示例文档的 link:https://github.com/spring-projects/spring-data-examples/tree/master/mongodb/query-by-example
此方法获取参数及其值的映射,并基于它形成查询。这个解决方案的明显缺点是形成的查询是基于 AND 的。因此,如果您需要一个仅由逻辑 AND 组成的动态查询,这就是可行的方法。我仍在处理具有所有逻辑操作的完全动态查询。写在Groovy:
Query buildSearchQuery(Map searchParams, List resultFields, int limit) {
Query query = new Query()
searchParams.each {
if (it.getValue() != null && it.getValue() != '') {
query.addCriteria(Criteria.where(it.getKey()).regex('^'+ it.getValue(), 'i'))
}
}
resultFields.each {
query.fields().include(it)
}
query.limit(limit)
query
}
我正在寻找一种动态创建参数化查询的方法。本质上,我想将查询与应用程序分离并使它们可配置。
例如,我想创建一个带有参数的查询,例如
{ firstName: ?0 }
您可能知道,这在 Spring 数据 mongodb 中使用接口是完全可行的:
interface MyQuery {
@Query("{firstName: ?0}")
public Person getByFirstName(final String name);
}
但这会在编译时耦合我的查询,而我想在运行时更改它们。
但是我找不到动态使用此机制的方法。幕后使用的特定 class 是 StringBasedMongoQuery
,并使用大量反射实用程序来确定查询、参数、返回类型等。您可以使用 [=66 管理大部分内容=] 我认为,但是注释似乎是泡菜。
这让我觉得我选择了错误的方法来解决这个问题。
我自己只看到了几个选项:
- 我自己为 mongo 重新实现 Spring 的花式查询解析(看起来工作量很大)
- 自己实现穷人的查询参数替换。 (这不灵活,不会在空参数上产生正确的结果...)
我在这里很茫然,我似乎不能单独使用StringBasedMongoQuery
太糟糕了,它似乎与反射耦合。
更新:
我的建议在这里似乎毫无意义,因为我还注意到 Spring 数据 mongodb 的 StringBasedMongoQuery
不支持 'dynamic' 具有可为空参数的查询;事实上,这是非常合乎逻辑的,当参数为 null 时,很难从查询中删除部分内容,而且完全不可能做到正确。
以下说明了这个问题:https://jira.spring.io/browse/DATAJPA-209
所以我可能的选择似乎已经转移到:
- 我们进行不同的查询,使用的查询取决于可用的参数。这可能会给维护带来噩梦。
- 我们公开了一个 groovy 或其他 JVM 脚本,允许我们使用 Spring 或 mongodb 的一个可用的 Criteria-esque API Java 驱动程序,它将为我们构建查询。这样做的缺点是现在想要配置查询的人至少必须知道 Java。
- 我们在需要时以牺牲可读性为代价使我们的查询空值安全。
与我在此 post 中提出的建议相比,我找到了解决此问题的更好、更简单的方法。我会告诉你我将如何解决这个问题,以供将来其他人参考。
虽然 mongodb 没有查询参数之类的东西,但我们可以通过使用 mongodb 提供的一些其他相当独特的构造以及严格的结构来使事情变得容易得多。我们可以坚持这种结构,因为它涉及用户的动态可配置查询。
我们的结构很简单,如下。我们有一个基本的查询结构,如下所示:
{ $and: []}
我们定义了一个将始终被填充的基本查询。这可以像空 JS 对象一样简单。这样,我们的查询就变成了
{ $and: [{}] }
所以,这只是查询所有内容。现在我们可以为每个可以动态的条件定义一个子查询。如果满足条件,我们可以选择将此查询添加到 $and
中。由于我们坚持将所有内容放在一个和构造中,我们知道查询的外观,并且我们不需要非常花哨的参数替换来获得非常灵活的东西来轻松工作。
一个查询可以变成例如:
{ $and: [{}, {active: true}, {started: false}]}
我们可以随时添加那些小子查询以添加和启动。
解决此问题的另一种方法是使用示例对象...我正在利用 spring-data-mongodb 顺便说一句,所以不确定这是否适用于您。 ..
public interface PetRepository extends MongoRepository<Pet, String>{
}
...
@Service
public class PetService {
private Logger logger = LogManager.getLogger(PetService.class);
@Autowired
PetRepository petRepository;
public List<Pet> findPetsByExample(String name, String sex, String color, Integer age){
//Guard clauses omitted...
Example<Pet> example = Example.of(new Pet(name, sex, color, age));
return Lists.newArrayList(petRepository.findAll(example));
}
现在它正在做的是利用在 MongoRepository 中扩展的 QueryByExampleExecutor 类在引擎盖下将提供的示例 pet 与您的文档内容进行比较。这将只查看我们的示例 pet 上已提供值的字段,并且将在比较中忽略空值。您还可以包括自定义匹配器以执行进一步的过滤。这是一些示例文档的 link:https://github.com/spring-projects/spring-data-examples/tree/master/mongodb/query-by-example
此方法获取参数及其值的映射,并基于它形成查询。这个解决方案的明显缺点是形成的查询是基于 AND 的。因此,如果您需要一个仅由逻辑 AND 组成的动态查询,这就是可行的方法。我仍在处理具有所有逻辑操作的完全动态查询。写在Groovy:
Query buildSearchQuery(Map searchParams, List resultFields, int limit) {
Query query = new Query()
searchParams.each {
if (it.getValue() != null && it.getValue() != '') {
query.addCriteria(Criteria.where(it.getKey()).regex('^'+ it.getValue(), 'i'))
}
}
resultFields.each {
query.fields().include(it)
}
query.limit(limit)
query
}