2012-09-06 7 views
8

En mi colección de libros mongodb tengo documentos estructurados de la siguiente manera:mongodb Agregación: Cómo devolver sólo los elementos coincidentes de una matriz

/* 0 */ 
{ 
    "_id" : ObjectId("50485b89b30f1ea69110ff4c"), 

    "publisher" : { 
    "$ref" : "boohya", 
    "$id" : "foo" 
    }, 
    "displayName" : "Paris Nightlife", 
    "catalogDescription" : "Some desc goes here", 
    "languageCode" : "en", 
    "rating" : 0, 
    "status" : "LIVE", 
    "thumbnailId" : ObjectId("50485b89b30f1ea69110ff4b"), 
    "indexTokens" : ["Nightlife", "Paris"] 
} 

realizo la siguiente consulta de expresiones regulares para encontrar todos los documentos que tienen de uno indexToken comenzando con " par":

{ "indexTokens" : { "$regex" : "^Par" , "$options" : "i"}} 

Si selecciono sólo el campo indexTokens a devolver la siguiente manera:

{ "indexTokens" : 1} 

DBOBJECT resultante es

{ "_id" : { "$oid" : "50485b89b30f1ea69110ff4c"} , "indexTokens" : [ "Nightlife" , "Paris"]} 

Lo que me gustaría conseguir es sólo el símbolo/tag que coincidía con la expresión regular (I don0t atención sobre cómo recuperar el documento en este punto, no necesito todas las etiquetas de la documento coincidente)

Este es un caso para el nuevo Marco de agregación resuelto bajo MongoDB v2.2. ?

Si es así, ¿cómo puedo modificar mi consulta para que el resultado real se vería así:

{ "indexTokens": [ "París", "Río del Paraíso", "Parma", etc ....] }

Pregunta adicional (¿tiene usted el código?): ¿Cómo lo hago utilizando el controlador Java?

Por ahora mi java parece:

DBObject query = new BasicDBObject("indexTokens", java.util.regex.Pattern.compile("^"+filter+"", Pattern.CASE_INSENSITIVE)); 
    BasicDBObject fields = new BasicDBObject("indexTokens",1); 
    DBCursor curs = getCollection() 
        .find(query, fields) 
        .sort(new BasicDBObject("indexTokens" , 1)) 
        .limit(maxSuggestionCount); 

Thx :)

EDIT:

Según sus respuestas he modificado mi código JAVA de la siguiente manera:

BasicDBObject cmdBody = new BasicDBObject("aggregate", "Book"); 
    ArrayList<BasicDBObject> pipeline = new ArrayList<BasicDBObject>(); 

    BasicDBObject match = new BasicDBObject("$match", new BasicDBObject("indexTokens", java.util.regex.Pattern.compile("^"+titleFilter+"", Pattern.CASE_INSENSITIVE))); 
    BasicDBObject unwind = new BasicDBObject("$unwind", "$indexTokens"); 
    BasicDBObject match2 = new BasicDBObject("$match", new BasicDBObject("indexTokens", java.util.regex.Pattern.compile("^"+titleFilter+"", Pattern.CASE_INSENSITIVE))); 
    BasicDBObject groupFilters = new BasicDBObject("_id",null); 
    groupFilters.append("indexTokens", new BasicDBObject("$push", "$indexTokens")); 
    BasicDBObject group = new BasicDBObject("$group", groupFilters); 

    pipeline.add(match); 
    pipeline.add(unwind); 
    pipeline.add(match2); 
    pipeline.add(group); 

    cmdBody.put("pipeline", pipeline); 



    CommandResult res = getCollection().getDB().command(cmdBody); 
    System.out.println(res); 

Qué salidas

{ "result" : [ { "_id" : null , "indexTokens" : [ "Paris"]}] , "ok" : 1.0} 

¡Esto es genio!

¡Muchas gracias!

Respuesta

10

Puede hacer esto con el marco de agregación 2.2. Algo como esto;

db.books.runCommand("aggregate", { 
    pipeline: [ 
     { // find docs that contain Par* 
      $match: { "indexTokens" : { "$regex" : "^Par" , "$options" : "i"}}, 
     }, 
     { // create a doc with a single array elemm for each indexToken entry 
      $unwind: "$indexTokens" 
     }, 
     { // now produce a list of index tokens 
      $group: { 
       _id: "$indexTokens", 
      }, 
     }, 
    ], 
}) 

O esto podría estar aún más cerca de lo que buscas si realmente quieres la matriz sin el documento;

db.books.runCommand("aggregate", { 
    pipeline: [ 
     { // find docs that contain Par* 
      $match: { "indexTokens" : { "$regex" : "^Par" , "$options" : "i"}}, 
     }, 
     { // create a doc with a single array elemm for each indexToken entry 
      $unwind: "$indexTokens" 
     }, 
     { // now throw out any unwind's that DON'T contain Par* 
      $match: { "indexTokens": { "$regex": "^Par", "$options": "i" } }, 
     }, 
     { // now produce the list of index tokens 
      $group: { 
       _id: null, 
       indexTokens: { $push: "$indexTokens" }, 
      }, 
     }, 
    ], 
}) 
+0

Puede agregar esto a su respuesta original como una segunda solución. De esta manera, las personas no se confundirán por qué hay dos respuestas tuyas :) – Sammaye

+0

ok. hecho eso ... – cirrus

+0

Gracias a ustedes dos funciona como un encanto. Agregué una respuesta para mostrar cómo lo hice en JAVA (no tengo el controlador más reciente, así que no pude usar el método aggregate() en DBCollection. – azpublic

2

Sobre la base de la respuesta de los cirrus, recomiendo hacer la primera $unwind para evitar la redundancia $match. Algo como:

db.books.aggregate(
    {$unwind:"$indexTokens"}, 
    {$match:{indexTokens:/^Par/}}, 
    {$group:{_id:null,indexTokens:{$push:"$indexTokens"}} 
}) 

¿Cómo se hace esto en Java? Puede usar el método DBCollection.aggregate(...) del controlador MongoDB v2.9.0. Cada operador de tubería, ej. $unwind o $match, corresponde a un objeto DBObject.

+1

En realidad, no creo que $ match sea redundante. $ unwind es que tendrá que crear un conjunto masivo de documentos en la RAM y desea reducir ese conjunto tan pronto como sea posible. El primer $ asegura que estamos SOLO trabajando con documentos que incluso tienen Par * en el indexTokens antes de que los desenrollemos. El segundo $ match luego corta ese conjunto a solo los que queremos. Recuerde, usted quiere obtener su $ match antes para reducir el volumen de la tubería. – cirrus

+1

Tiene razón. Elimine los no hacer coincidir los documentos, desenrollar las matrices y luego volver a coincidir para eliminar los documentos que no coinciden con la expresión regular. – slee

Cuestiones relacionadas