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"
}
}