Jason Winnebeck
Jason Winnebeck

Reputation: 1274

Provide limit on Spring Data Mongo repository

Using the latest Spring Data Mongo (2.1.1 at time of writing), how do I specify to get the first record of a "custom" query method? Here is an example:

@Query(value="{name: ?0, approval: {'$ne': null}}",
        sort="{'approval.approvedDate': -1}",
        fields = "{ _id: 1 }")
List<Item> getLatestApprovedIdByName(String name, Pageable pageable);

/**
 * Finds the id of the most recently approved document with the given name.
 */
default Item getLatestApprovedIdByName(String name) {
    return getLatestApprovedIdByName(name, PageRequest.of(0, 1)).stream()
                                                                .findFirst()
                                                                .orElse(null);
}

Ideally I could just annotate getLatestApprvedIdByName taking only the String parameter.

There doesn't seem to be a limit field on the org.springframework.data.mongodb.repository.Query annotation.

It seems odd because I can emulate everything the named methods do except findFirst.

Without the Pageable, I get IncorrectResultSizeDataAccessException, and returning a List is not acceptable because I don't want to waste time returning an arbitrarily large result, plus the complicated code needing to deal with the possibility of 0 or 1 items.

Upvotes: 8

Views: 21869

Answers (2)

Seyed Ali Roshan
Seyed Ali Roshan

Reputation: 1574

as I had the same issue; I did find @Aggregation a more useful and cleaner solution you can do this:

@Aggregation({
    "{$match: { name: ?0, approval: {$ne: null} }",
    "{$sort: { _id: 1 } }",
    "{$limit: 1 }"
})
Optional<Item> getLatestApprovedIdByName(String name);

Upvotes: 2

Sync
Sync

Reputation: 3777

Because your query returns multiple documents, there's no way to make it return a single Item directly.

Using Stream

// Repository
@Query(value="{name: ?0, approval: {'$ne': null}}",
        sort="{'approval.approvedDate': -1}",
        fields = "{ _id: 1 }")
Stream<Item> getLatestApprovedIdByName(String name);

// Service
default Item getLatestApprovedIdByName(String name) {
    return getLatestApprovedIdByName(name).stream().findFirst().orElse(null);
}

Due to the way Stream works, you'll only fetch the first query result instead of the entire result set. For more information, please see the documentation.

Using Page and Pageable

// Repository
@Query(value = "{name: ?0, approval: {'$ne': null}}", fields = "{ _id: 1 }")
Page<Item> getLatestApprovedIdByName(String name, Pageable pageable);

// Service
default Item getLatestApprovedIdByName(String name) {
    PageRequest request = new PageRequest(0, 1, new Sort(Sort.Direction.DESC, "approval.approvedDate"));
    return getLatestApprovedIdByName(name, request).getContent().get(0);
}

By making use of PageRequest, you can specify how many results you want as well as specify the sort order. Based on this answer.

Upvotes: 11

Related Questions