2012-10-07 21 views
5

que tiene una colección de documentos de la siguiente forma:Clasificación por relevancia con MongoDB

{ _id: ObjectId(...) 
, title: "foo" 
, tags: ["bar", "baz", "qux"] 
} 

La consulta debe encontrar todos los documentos con cualquiera de estas etiquetas. Actualmente uso esta consulta:

{ "tags": { "$in": ["bar", "hello"] } } 

Y funciona; se devuelven todos los documentos etiquetados "bar" u "hola".

Sin embargo, quiero ordenar por relevancia, es decir, cuanto más coincidan con las etiquetas cuanto antes se produzca el documento en el resultado. Por ejemplo, un documento etiquetado ["bar", "hello", "baz"] debe ser más alto en los resultados que un documento etiquetado ["bar", "baz", "boo"] para la consulta ["bar", "hello"]. ¿Cómo puedo conseguir esto?

Respuesta

9

MapReduce y hacerlo del lado del cliente va a ser demasiado lento - te . debe utilizar el marco de la agregación (nuevo en MongoDB 2.2)

podría ser algo como esto:

db.collection.aggregate([ 
    { $match : { "tags": { "$in": ["bar", "hello"] } } }, 
    { $unwind : "$tags" }, 
    { $match : { "tags": { "$in": ["bar", "hello"] } } }, 
    { $group : { _id: "$title", numRelTags: { $sum:1 } } }, 
    { $sort : { numRelTags : -1 } } 
    // optionally 
    , { $limit : 10 } 
]) 

Nota el primer y tercer miembros de tuberías se ven idéntico, esto es intencional y necesario. Esto es lo que hacen los pasos:

  1. solo pasan documentos que tienen la etiqueta "bar" u "hola" en ellos.
  2. desenrollar la matriz de etiquetas (es decir, dividido en un documento por elemento de etiquetas
  3. pase en sólo etiquetas exactamente "bar" o "Hola" (es decir, desechar el resto de las etiquetas)
  4. grupo por título (que podría ser también por "$ _id" o cualquier otra combinación de documento original sumando el número de etiquetas (de "bar" y "hola") que tenía
  5. ordenar en orden descendente según el número de etiquetas relevantes
  6. límite (opcional) el conjunto devuelto al principio 10.
+0

Creo que es {$ unwind: "$ tags"} en lugar de {$ unwind: {"$ tags"}} –

+0

¿Qué sabe usted - tiene razón, el primero en darse cuenta en cuatro años? :) –

+0

Supongo. Respuesta increíble por cierto-- gran ayuda. Gracias. –

1

Podría utilizar MapReduce para algo así. Procesaría cada documento en el paso Mapa, descubriendo cuántas etiquetas coinciden con la consulta y asignando una puntuación. Entonces puedes ordenar según ese puntaje.

http://www.mongodb.org/display/DOCS/MapReduce

0

Algo tan complejo debe hacerse después de la consulta. Ya sea desde el servidor a través de db.eval (si su cliente admite esto) o solo desde el lado del cliente. Aquí hay un ejemplo de lo que estás buscando.

Recuperará todas las publicaciones con las etiquetas que haya especificado y las ordenará de acuerdo con la cantidad de coincidencias.

quitar el db.eva (parte y traducirlo a la lengua de su cliente utiliza para consultar para obtener el efecto del lado del cliente (

db.eval(function() { 
    var tags = ["a","b","c"]; 
    return db.posts.find({tags:{$in:tags}}).toArray().sort(function(a,b){ 

     var matches_a = 0; 
     var matches_b = 0; 
     a.tags.forEach(function (tag) { 
      for (t in tags) { 
       if (tag == t) { 
        matches_a++; 
       } else { 
        matches_b++; 
       } 
      } 
     }); 

     b.tags.forEach(function(tag) { 
      for (t in tags) { 
       if (tag == t) { 
        matches_b++; 
       } else { 
        matches_a++; 
       } 
      } 
     }); 
     return matches_a - matches_b; 
    }); 
}); 
+1

Esto es lento para grandes colecciones, así que intentaré encontrar otra respuesta. – arian

Cuestiones relacionadas