34

我有两个域对象,

@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 更新“类型”,所以我在自定义 Repository 接口中编写了一个 findBy 查询方法,

public interface CustomRepository extends MongoRepository<PracticeQuestion, String> {

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

我的问题是,当我使用 userId 为 1 和 questionID 为 3 执行此方法时,它会返回整个问题列表,而与 questionID 无关。查询方法名称是否有效或者我应该如何编写嵌套对象的查询。

感谢您的任何建议。

4

4 回答 4

40

只需@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.
}
于 2012-10-10T14:04:12.393 回答
22

属性表达式

属性表达式只能引用托管实体的直接属性,如前面的示例所示。在创建查询时,您已经确保解析的属性是托管域类的属性。但是,您也可以通过遍历嵌套属性来定义约束。假设人员有带有邮政编码的地址。在这种情况下,方法名称为List<Person> findByAddressZipCode(ZipCode zipCode); 创建属性遍历 x.address.zipCode。解析算法首先将整个部分 (AddressZipCode) 解释为属性,并检查域类中具有该名称的属性(未大写)。如果算法成功,它将使用该属性。如果不是,该算法将源在驼峰部分从右侧拆分为头部和尾部,并尝试找到相应的属性,在我们的示例中为 AddressZip 和 Code。如果算法找到具有该头部的属性,它将获取尾部并继续从那里向下构建树,以刚才描述的方式将尾部拆分。如果第一个拆分不匹配,则算法将拆分点向左移动(地址、邮政编码)并继续。

虽然这应该适用于大多数情况,但算法可能会选择错误的属性。假设 Person 类也有一个 addressZip 属性。该算法已经在第一轮拆分中匹配,本质上选择了错误的属性并最终失败(因为 addressZip 的类型可能没有 code 属性)。要解决这种歧义,您可以在方法名称中使用 _ 来手动定义遍历点。所以我们的方法名最终会是这样的:

用户数据存储库:

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

对于您的存储库类中的上述 Document/POJO:

UserData findByProfile_Email(String email);

参考:http ://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html

于 2015-01-19T08:51:34.363 回答
5

您需要使用 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"
             }
 }
于 2017-09-26T18:36:23.563 回答
0

我也有类似的问题。为此,我在嵌套类属性之前添加了 $。试试下面的查询

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

于 2021-04-09T10:17:27.217 回答