hacer - relacionar dos colecciones en mongodb
datos de primavera-Mongodb-método FindBy para objetos anidados (3)
Tengo dos objetos de dominio,
@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
}
Mi documento JSON es así,
{
"_id" : ObjectId("506d9c0ce4b005cb478c2e97"),
"userId" : 1,
"questions" : [
{
"questionID" : 1,
"type" : "optional"
},
{
"questionID" : 3,
"type" : "mandatory"
}
]
}
Tengo que actualizar el "tipo" basado en userId y questionId, por lo que he escrito un método de consulta findBy dentro de la interfaz personalizada del repositorio,
public interface CustomRepository extends MongoRepository<PracticeQuestion, String> {
List<PracticeQuestion> findByUserIdAndQuestionsQuestionID(int userId,int questionID);
}
Mi problema es cuando ejecuto este método con userId como 1 y questionID como 3, devuelve toda la lista de preguntas independientemente de la questionID. ¿Es válido el nombre del método de consulta o cómo debo escribir la consulta para objetos anidados?
Gracias por cualquier sugerencia.
Expresiones de propiedad
Las expresiones de propiedad solo pueden referirse a una propiedad directa de la entidad administrada, como se muestra en el ejemplo anterior. En el momento de la creación de consultas, ya se asegura de que la propiedad analizada sea una propiedad de la clase de dominio administrado. Sin embargo, también puede definir restricciones atravesando propiedades anidadas. Supongamos que las personas tienen direcciones con códigos postales. En ese caso un nombre de método de
Lista findByAddressZipCode (ZipCode zipCode); Crea la propiedad traversal x.address.zipCode. El algoritmo de resolución comienza con la interpretación de toda la parte (AddressZipCode) como la propiedad y verifica la clase de dominio para una propiedad con ese nombre (sin capitalizar). Si el algoritmo tiene éxito, utiliza esa propiedad. Si no, el algoritmo divide la fuente en las partes de la caja del camello desde el lado derecho en una cabeza y una cola e intenta encontrar la propiedad correspondiente, en nuestro ejemplo, AddressZip y Code. Si el algoritmo encuentra una propiedad con esa cabeza, toma la cola y continúa construyendo el árbol desde allí, dividiendo la cola de la manera que acabamos de describir. Si la primera división no coincide, el algoritmo mueve el punto de división a la izquierda (Dirección, Código postal) y continúa.
Aunque esto debería funcionar para la mayoría de los casos, es posible que el algoritmo seleccione la propiedad incorrecta. Supongamos que la clase Persona tiene también una propiedad addressZip. El algoritmo coincidiría en la primera ronda dividida y esencialmente elegiría la propiedad incorrecta y finalmente fallaría (ya que el tipo de addressZip probablemente no tiene propiedad de código). Para resolver esta ambigüedad, puede usar _ dentro del nombre de su método para definir manualmente los puntos de recorrido. Así que nuestro nombre de método terminaría así:
UserDataRepository:
Lista findByAddress_ZipCode (ZipCode zipCode);
UserData findByUserId (String userId);
Repositorio del perfil:
Perfil findByProfileId (String profileId);
UserDataRepositoryImpl:
UserData userData = userDateRepository.findByUserId (userId);
Perfil profile = profileRepository.findByProfileId (userData.getProfileId ());
userData.setProfile (perfil);
Muestra de 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;
}
Para el documento / POJO anterior en su clase de repositorio:
UserData findByProfile_Email (String email);
Para ref: http://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html
Necesitas usar el marco de agregación de Mongo:
1) Crear un método personalizado para el repositorio de Mongo: Agregar un método personalizado al Repositorio
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) Es necesario crear una clase (porque la operación de desenrollado ha cambiado la estructura de la clase) como a continuación:
public class PracticeQuestionUnwind {
private String userId;
private Question questions;
Esto le dará solo aquellos resultados que coincidan con el userId
y el userId
questionId
Resultado para userId: 1 y questionId: 111:
{
"userId": "1",
"questions": {
"questionId": "111",
"type": "optional"
}
}
Solo usa la anotación @Query
en ese método.
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);
}
Al agregar los fields
forman parte de la anotación @Query
, le está diciendo a Mongo que solo devuelva esa parte del documento. Sin embargo, tenga en cuenta que todavía devuelve todo el documento en el mismo formato, solo que falta todo lo que no especificó. Por lo tanto, su código todavía tendrá que devolver la List<PracticeQuestion>
y tendrá que hacer:
foreach (PracticeQuestion pq : practiceQuestions) {
Question q = pq.getQuestions().get(0); // This should be your question.
}