J'ai deux objets de domaine,
@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
}
Mon doc JSON est comme ça,
{
"_id" : ObjectId("506d9c0ce4b005cb478c2e97"),
"userId" : 1,
"questions" : [
{
"questionID" : 1,
"type" : "optional"
},
{
"questionID" : 3,
"type" : "mandatory"
}
]
}
Je dois mettre à jour le "type" en fonction de userId et de questionId. J'ai donc écrit une méthode de requête findBy dans l'interface de référentiel personnalisée.
public interface CustomRepository extends MongoRepository<PracticeQuestion, String> {
List<PracticeQuestion> findByUserIdAndQuestionsQuestionID(int userId,int questionID);
}
Mon problème est que lorsque j'exécute cette méthode avec userId en tant que 1 et questionID en tant que 3, il renvoie la liste complète des questions indépendamment de l'ID de question. Le nom de la méthode de requête est-il valide ou comment dois-je écrire la requête pour les objets imbriqués.
Merci pour toute suggestion.
Utilisez simplement l'annotation @Query
sur cette méthode.
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);
}
En ajoutant la partie fields
de l'annotation @Query
, vous indiquez à Mongo de ne renvoyer que cette partie du document. Attention, il renvoie toujours le document entier dans le même format - il manque juste tout ce que vous n'avez pas spécifié. Donc, votre code devra toujours retourner List<PracticeQuestion>
et vous devrez faire:
foreach (PracticeQuestion pq : practiceQuestions) {
Question q = pq.getQuestions().get(0); // This should be your question.
}
Expressions de propriété
Les expressions de propriété ne peuvent faire référence qu'à une propriété directe de l'entité gérée, comme illustré dans l'exemple précédent. Au moment de la création de la requête, vous vous assurez déjà que la propriété analysée est une propriété de la classe de domaine géré. Cependant, vous pouvez également définir des contraintes en parcourant des propriétés imbriquées. Supposons que des personnes aient des adresses avec des codes ZIP. Dans ce cas, un nom de méthode de
Liste findByAddressZipCode (ZipCode zipCode); Crée la propriété traversal x.address.zipCode. L'algorithme de résolution commence par interpréter la totalité de la pièce (AddressZipCode) en tant que propriété et recherche dans la classe de domaine une propriété portant ce nom (non capitalisé). Si l'algorithme réussit, il utilise cette propriété. Si ce n'est pas le cas, l'algorithme divise la source au niveau des parties de la casse du chameau du côté droit en une tête et une queue et tente de trouver la propriété correspondante, dans notre exemple, AddressZip et Code. Si l'algorithme trouve une propriété avec cette tête, il prend la queue et continue à construire l'arbre à partir de là, en séparant la queue de la manière décrite ci-dessus. Si la première division ne correspond pas, l'algorithme déplace le point de division vers la gauche (Address, ZipCode) et continue.
Bien que cela devrait fonctionner dans la plupart des cas, il est possible que l'algorithme sélectionne la mauvaise propriété. Supposons que la classe Person possède également une propriété addressZip. L'algorithme correspondrait déjà dans le premier tour et choisirait essentiellement la mauvaise propriété, puis échouerait (car le type addressZip n'a probablement pas de propriété de code). Pour résoudre cette ambiguïté, vous pouvez utiliser _ dans le nom de votre méthode pour définir manuellement les points de traversée. Donc notre nom de méthode finirait comme suit:
UserDataRepository:
Liste findByAddress_ZipCode (ZipCode zipCode);
UserData findByUserId (String userId);
ProfileRepository:
Profil findByProfileId (String profileId);
UserDataRepositoryImpl:
UserData userData = userDateRepository.findByUserId (userId);
Profil profil = profileRepository.findByProfileId (userData.getProfileId ());
userData.setProfile (profil);
Sample 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;
}
Pour le document/POJO ci-dessus dans votre classe de référentiel:
UserData findByProfile_Email (Chaîne email);
Pour référence: http://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html
Vous devez utiliser le framework Mongo Aggregation:
1) Créer une méthode personnalisée pour le référentiel mongo: Ajouter une méthode personnalisée au référentiel
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) Vous devez créer une classe (car l'opération de déroulement a changé la structure de la classe) comme ci-dessous:
public class PracticeQuestionUnwind {
private String userId;
private Question questions;
Cela ne vous donnera que le résultat qui correspond à fournir userId
et questionId
Résultat pour userId: 1 et questionId: 111:
{
"userId": "1",
"questions": {
"questionId": "111",
"type": "optional"
}
}