2014-07-17 3 views
1

J'ai un schemea qui crée des documents en utilisant la structure suivante:agrégation MongoDB, trouvant dans un tableau de valeurs

{ 
    "_id" : "2014-07-16:52TEST", 
    "date" : ISODate("2014-07-16T23:52:59.811Z"), 
    "name" : "TEST" 
    "values" : [ 
     [ 
      1405471921000, 
      0.737121 
     ], 
     [ 
      1405471922000, 
      0.737142 
     ], 
     [ 
      1405471923000, 
      0.737142 
     ], 
     [ 
      1405471924000, 
      0.737142 
     ] 
    ] 
} 

Dans les valeurs, le premier indice est un horodatage. Ce que j'essaie de faire est d'interroger un horodatage spécifique pour trouver la valeur la plus proche ($gte).

J'ai essayé la requête globale suivante:

[ 

     { "$match": { 
      "values": { 
       "$elemMatch": { "0": {"$gte": 1405471923000} } 
      }, 
      "name" : 'TEST' 
     }}, 

     { "$project" : { 
      "name" : 1, 
      "values" : 1 
     }}, 

     { "$unwind": "$values" }, 

     { "$match": { "values.0": { "$gte": 1405471923000 } } }, 

     { "$limit" : 1 }, 

     { "$sort": { "values.0": -1 } }, 

     { "$group": { 
      "_id": "$name", 
      "values": { "$push": "$values" }, 
     }} 



    ] 

Cela semble fonctionner, mais il ne tire pas la valeur la plus proche. Il semble tirer quelque chose de plus grand ou égal à et le genre ne semble pas être appliqué, de sorte qu'il va tirer un horodatage qui est loin dans le futur.

Toutes les suggestions seraient super!

Merci

Répondre

0

Il y a quelques choses de mal à l'approche ici, même si elle est un effort équitable. Vous avez raison, vous avez besoin de $sort ici, mais le problème est que vous ne pouvez pas "trier" sur un élément interne avec un tableau. Afin d'obtenir une valeur qui peut être triée, vous devez d'abord $unwind le tableau car il ne triera pas autrement sur une position de tableau.

Vous ne voulez certainement pas $limit dans le pipeline. Vous pouvez tester cela sur un seul document, mais "limiter" agira réellement sur l'ensemble des documents en cours. Donc, si plus d'un document correspondait à votre condition, ils seraient jetés. La principale chose que vous voulez faire ici est d'utiliser $first dans votre étape $group, qui est appliquée une fois que vous avez trié pour obtenir l'élément "le plus proche" que vous voulez.

db.collection.aggregate([ 

    // Documents that have an array element matching the condition 
    { "$match": { 
     "values": { "$elemMatch": { "0": {"$gte": 1405471923000 } } } 
    }}, 

    // Unwind the top level array 
    { "$unwind": "$values" }, 

    // Filter just the elements that match the condition 
    { "$match": { "values.0": { "$gte": 1405471923000 } } }, 

    // Take a copy of the inner array 
    { "$project": { 
     "date": 1, 
     "name": 1, 
     "values": 1, 
     "valCopy": "$values" 
    }}, 

    // Unwind the inner array copy 
    { "$unwind": "$valCopy" }, 

    // Filter the inner elements 
    { "$match": { "valCopy": { "$gte": 1405471923000 } }}, 

    // Sort on the now "timestamp" values ascending for nearest 
    { "$sort": { "valCopy": 1 } }, 

    // Take the "first" values 
    { "$group": { 
     "_id": "$_id", 
     "date": { "$first": "$date" }, 
     "name": { "$first": "$name" }, 
     "values": { "$first": "$values" }, 
    }}, 

    // Optionally push back to array to match the original structure 
    { "$group": { 
     "_id": "$_id", 
     "date": { "$first": "$date" }, 
     "name": { "$first": "$name" }, 
     "values": { "$push": "$values" }, 
    }} 
]) 

Et cela produit votre document avec juste la valeur d'horodatage "le plus proche" correspondant à la forme de document original:

{ 
    "_id" : "2014-07-16:52TEST", 
    "date" : ISODate("2014-07-16T23:52:59.811Z"), 
    "name" : "TEST", 
    "values" : [ 
      [ 
        1405471923000, 
        0.737142 
      ] 
    ] 
} 
Questions connexes