使用Spring数据mongodb的动态参数化查询

时间:2015-11-12 13:57:41

标签: mongodb dynamic parameters spring-data spring-data-mongodb

我正在寻找一种动态创建参数化查询的方法。实质上,我想将查询与应用程序分离,并使它们可配置。

例如,我想创建一个带参数的查询,例如

{ firstName: ?0 }

正如您可能知道的,这在使用接口的Spring数据mongodb中非常可行:

interface MyQuery {

  @Query("{firstName: ?0}")
  public Person getByFirstName(final String name);
}

但是这会在编译时耦合我的查询,而我想在运行时更改它们。

但我无法找到一种动态使用此机制的方法。引擎盖下使用的特定类是StringBasedMongoQuery,并使用重型反射实用程序来确定查询,参数,返回类型等。您可以使用Groovy管理大部分内容我认为,但注释似乎成为一个泡菜。

这让我觉得我选择了错误的方法来解决这个问题。

我自己只看到一些选项:

  • Reimplement Spring为我自己的mongo解析花哨的查询(看起来很多工作)
  • 自己实施穷人的查询参数替换。 (这不灵活,不会在空参数上产生正确的结果......)

我在这里失去了,这太糟糕了,我似乎无法单独使用StringBasedMongoQuery,它似乎与反思有关。

更新

我的建议在这里似乎毫无意义,因为我注意到Spring数据mongodb的StringBasedMongoQuery不支持带有可空参数的'动态'查询;事实上,这是非常合乎逻辑的,当参数为空时,很难从查询中删除部分,并且很难做到这一点。

以下说明了这个问题:https://jira.spring.io/browse/DATAJPA-209

所以看来我可能的选择已转移到:

  • 我们进行不同的查询,使用的查询取决于可用的参数。这可能会造成维护的噩梦。
  • 我们公开了一个groovy或其他JVM脚本,它允许我们使用Spring的一个可用的Criteria-esque API或mongodb Java驱动程序,然后为我们构建查询。这样做的缺点是,想要配置查询的人现在必须至少知道Java。
  • 我们在需要时以可读性为代价使我们的查询为null安全。

3 个答案:

答案 0 :(得分:1)

此方法获取参数及其值的映射,并基于该参数形成查询。此解决方案的明显缺点是,形成的查询基于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
}

答案 1 :(得分:0)

我发现这个问题比我在这篇文章中已经提出的建议更好更容易。我将告诉您如何解决此问题以供将来参考。

虽然mongodb没有像查询参数或类似的东西,但我们可以通过使用mongodb提供的其他一些相当独特的结构以及严格的结构来使事情变得更容易。我们可以遵循这种结构,因为它涉及用户的动态可配置查询。

我们的结构很简单,如下所示。我们有一个基本的查询结构,如下所示:

{ $and: []}

我们定义一个将始终填充的基本查询。这可以像空JS对象一样简单。像这样,我们的查询变为

{ $and: [{}] }

所以,这只是查询一切。现在我们可以为每个可以动态的条件定义一个子查询。如果符合条件,我们可以选择将此查询添加到$and。由于我们将所有内容都放在一个构造中,我们知道查询的外观,并且我们不需要非常奇特的参数替换来获得非常灵活的工作方式。

查询可以成为例如:

{ $and: [{}, {active: true}, {started: false}]}

我们可以添加这些小子查询以便随时添加和启动。

答案 2 :(得分:0)

你可以解决这个问题的另一种方法是使用Example对象...我正在利用spring-data-mongodb btw所以不确定这是否适用于你......

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中扩展到底层的QueryByExampleExecutorclass,将提供的示例pet与文档内容进行比较。这只会查看我们的示例宠物上提供了值的字段,并会忽略它的比较中的Null。您还可以包括自定义匹配器以执行进一步过滤。以下是一些示例文档的链接:https://github.com/spring-projects/spring-data-examples/tree/master/mongodb/query-by-example