Spring data - Mongodb - метод findBy для вложенных объектов

У меня есть два объекта домена,

@Document
public class PracticeQuestion {

     private int userId;
     private List<Question> questions;

// Getters and setters
}

@Document
public class Question {

     private int questionID;
     private String type;

// Getters and setters
}

Мой JSON файл такой,

{
    "_id" : ObjectId("506d9c0ce4b005cb478c2e97"),
    "userId" : 1,
    "questions" : [
        {
            "questionID" : 1,
            "type" : "optional"

         },
        {
             "questionID" : 3,
             "type" : "mandatory"
        }
    ]
}

Мне нужно обновить "тип" на основе userId и questionId, поэтому я написал метод запроса findBy внутри настраиваемого интерфейса репозитория,

public interface CustomRepository extends MongoRepository<PracticeQuestion, String> {

    List<PracticeQuestion> findByUserIdAndQuestionsQuestionID(int userId,int questionID);       
}

Моя проблема заключается в том, когда я выполняю этот метод с userId как 1 и questionID как 3, он возвращает весь список вопросов независимо от идентификатора questionID. Является ли имя метода запроса действительным или как я должен писать запрос для вложенных объектов.

Спасибо за любое предложение.

Ответ 1

Просто используйте аннотацию @Query для этого метода.

public interface CustomRepository extends MongoRepository<PracticeQuestion, String> {

    @Query(value = "{ 'userId' : ?0, 'questions.questionID' : ?1 }", fields = "{ 'questions.questionID' : 1 }")
    List<PracticeQuestion> findByUserIdAndQuestionsQuestionID(int userId, int questionID);

}

Добавив часть fields аннотации @Query, вы сообщаете Mongo только о возврате этой части документа. Остерегайтесь, однако, он все равно возвращает весь документ в том же формате - просто отсутствует все, что вы не указали. Таким образом, вашему коду все равно придется возвращать List<PracticeQuestion>, и вам нужно будет:

foreach (PracticeQuestion pq : practiceQuestions) {
    Question q = pq.getQuestions().get(0); // This should be your question.
}

Ответ 2

Выражения свойств

Выражения свойств могут относиться только к прямому свойству управляемого объекта, как показано в предыдущем примере. Во время создания запроса вы уже убедитесь, что свойство parsed является свойством управляемого класса домена. Однако вы также можете определить ограничения путем перемещения вложенных свойств. Предположим, что у лиц есть адреса с ZipCodes. В этом случае имя метода

Список findByAddressZipCode (ZipCode zipCode); создает обход свойства x.address.zipCode. Алгоритм разрешения начинается с интерпретации всей части (AddressZipCode) в качестве свойства и проверяет класс домена для свойства с этим именем (некапитализированный). Если алгоритм преуспевает, он использует это свойство. Если нет, алгоритм разбивает источник на частях верблюда с правой стороны на голову и хвост и пытается найти соответствующее свойство, в нашем примере AddressZip и Code. Если алгоритм находит свойство с этой головой, он берет хвост и продолжает строить дерево вниз, отделяя хвост так, как только что описано. Если первый сплит не соответствует, алгоритм перемещает точку разделения влево (адрес, ZipCode) и продолжается.

Хотя это должно работать в большинстве случаев, алгоритм может выбрать неправильное свойство. Предположим, что класс Person также имеет свойство addressZip. Алгоритм будет соответствовать в первом раунде раскола уже и по существу выбрать неправильное свойство и, наконец, сбой (поскольку тип addressZip, вероятно, не имеет свойства кода). Чтобы устранить эту двусмысленность, вы можете использовать _ внутри имени метода, чтобы вручную определять точки обхода. Итак, имя нашего метода закончится так:

UserDataRepository:

Список findByAddress_ZipCode (ZipCode zipCode);

UserData findByUserId (String userId);

ProfileRepository:

Профиль findByProfileId (String profileId);

UserDataRepositoryImpl:

UserData userData = userDateRepository.findByUserId(userId);

Профиль профиля = профильRepository.findByProfileId(userData.getProfileId());

userData.setProfile(профиль);

Пример Pojo:

открытый класс UserData {

private String userId;
private String status;
private Address address;
private String profileId;

//New Property
private Profile profile;

//TODO:setter & getter

}

public class Profile {

private String email;
private String profileId;

}

Для вышеуказанного документа /POJO в вашем классе репозитория:

UserData findByProfile_Email (String email);

Для ссылки: http://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html

Ответ 3

Вам нужно использовать структуру агрегации Mongo:

1) Создайте собственный метод для репозитория mongo: Добавить настраиваемый метод в репозиторий

UnwindOperation unwind =  Aggregation.unwind("questions");
MatchOperation match = Aggregation.match(Criteria.where("userId").is(userId).and("questions.questionId").is(questionID));
Aggregation aggregation = Aggregation.newAggregation(unwind,match);
AggregationResults<PracticeQuestionUnwind> results = mongoOperations.aggregate(aggregation, "PracticeQuestion",
                PracticeQuestionUnwind.class);
return results.getMappedResults();

2) Вам нужно создать класс (потому что операция размотки изменила структуру класса), как показано ниже:

public class PracticeQuestionUnwind {
    private String userId;
    private Question questions;

Это даст вам только те результаты, которые соответствуют запросу userId и questionId

Результат для userId: 1 и questionId: 111:

{
    "userId": "1",
     "questions": {
                "questionId": "111",
                "type": "optional"
             }
 }