данные весны - 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);       
}

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

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


person user1720083    schedule 04.10.2012    source источник


Ответы (4)


Просто используйте аннотацию @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.
}
person sbzoom    schedule 10.10.2012
comment
Спасибо за ответ, но снова этот запрос возвращает массив, а не соответствующий элемент в массиве. - person user1720083; 12.10.2012
comment
Ой. Я понимаю что ты имеешь ввиду. Вы можете вернуть только весь документ. Запрос фактически ищет документы с этим идентификатором вопроса. Но вы всегда получаете весь документ, а не только вопрос. Это не java, это MongoDB. Посмотрите на этот вопрос/ответ для уточнения: stackoverflow.com/a/3985982/229178 - person sbzoom; 12.10.2012
comment
После некоторой домашней работы я понял, что вы можете указывать частичные объекты в части fields вашего запроса (projections). mongodb.org/display/DOCS/Retrieving+a+Subset+ of+Fields Я обновлю свой ответ выше, чтобы привести пример. - person sbzoom; 12.10.2012
comment
Спасибо, а пока тоже попробую. - person user1720083; 15.10.2012
comment
Это работает и для ReactiveMongoRepository? У меня это не работает: @Query({'properties.brand' : ?0, 'properties.capacity' : ?1 }) Flux‹Offer› findAllByPropertiesBrandAndPropertiesCapacity(String brand, String вместимость); - person Matley; 07.10.2019
comment
Я не использовал Mongo уже много лет, и я только игрался с Flux. Запрос должен работать, но я не уверен, как Flux его вернет. Извини. - person sbzoom; 07.10.2019

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

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

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

Репозиторий данных пользователя:

List<UserData> findByAddress_ZipCode(ZipCode zipCode);

UserData findByUserId(String userId);

Репозиторий профилей:

Profile findByProfileId(String profileId);

UserDataRepositoryImpl:

UserData userData =  userDateRepository.findByUserId(userId);

Profile profile = profileRepository.findByProfileId(userData.getProfileId());

userData.setProfile(profile);

Образец Pojo:

public class 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(строка электронной почты);

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

person Govardhana Rao Ganji    schedule 19.01.2015
comment
это качественный пост - person ZZ 5; 23.04.2018
comment
Это точный ответ на этот вопрос. - person Monis; 23.05.2019

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

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

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"
             }
 }
person Mehraj Malik    schedule 26.09.2017

у меня тоже была похожая проблема. для этого я добавил $ перед атрибутами вложенного класса. попробуйте запрос ниже

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

person Vinay Mohaniya    schedule 09.04.2021