MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

MongoDb :trova l'elemento comune da due array all'interno di una query

Ci sono alcuni approcci per fare quello che vuoi, dipende solo dalla tua versione di MongoDB. Inviando solo le risposte della shell. Il contenuto è fondamentalmente una rappresentazione JSON che non è difficile da tradurre per le entità DBObject in Java o JavaScript da eseguire sul server in modo che non cambi davvero.

Il primo e il più veloce approccio è con MongoDB 2.6 e versioni successive, dove ottieni le nuove operazioni sugli insiemi:

var test = [ "t3", "t4", "t5" ];

db.collection.aggregate([
   { "$match": { "tags": {"$in": test } }},
   { "$project": {
       "tagMatch": {
           "$setIntersection": [
               "$tags",
               test
           ]
       },
       "sizeMatch": {
           "$size": {
               "$setIntersection": [
                   "$tags",
                   test
               ]
           }
       }
   }},
   { "$match": { "sizeMatch": { "$gte": 1 } } },
   { "$project": { "tagMatch": 1 } }
])

I nuovi operatori sono $setIntersection che sta facendo il lavoro principale e anche il $size operatore che misura la dimensione dell'array e aiuta per quest'ultimo filtraggio. Questo finisce come un confronto di base di "set" per trovare gli elementi che si intersecano.

Se hai una versione precedente di MongoDB, questo è ancora possibile, ma hai bisogno di alcune fasi in più e ciò potrebbe influire in qualche modo sulle prestazioni a seconda se hai array di grandi dimensioni:

var test = [ "t3", "t4", "t5" ];

db.collection.aggregate([
   { "$match": { "tags": {"$in": test } }},
   { "$project": {
      "tags": 1,
      "match": { "$const": test }
   }},
   { "$unwind": "$tags" },
   { "$unwind": "$match" },
   { "$project": {
       "tags": 1,
       "matched": { "$eq": [ "$tags", "$match" ] }
   }},
   { "$match": { "matched": true }},
   { "$group": {
       "_id": "$_id",
       "tagMatch": { "$push": "$tags" },
       "count": { "$sum": 1 }
   }}
   { "$match": { "count": { "$gte": 1 } }},
   { "$project": { "tagMatch": 1 }}
])

Oppure, se tutto ciò sembra essere coinvolto o i tuoi array sono abbastanza grandi da fare la differenza in termini di prestazioni, c'è sempre mapReduce :

var test = [ "t3", "t4", "t5" ];

db.collection.mapReduce(
    function () {
      var intersection = this.tags.filter(function(x){
          return ( test.indexOf( x ) != -1 );
      });
      if ( intersection.length > 0 ) 
          emit ( this._id, intersection );
   },
   function(){},
   {
       "query": { "tags": { "$in": test } },
       "scope": { "test": test },
       "output": { "inline": 1 }
   }
)

Tieni presente che in tutti i casi $in l'operatore aiuta comunque a ridurre i risultati anche se non è la corrispondenza completa. L'altro elemento comune è controllare la "dimensione" del risultato dell'intersezione per ridurre la risposta.

Tutto abbastanza facile da codificare, convinci il capo a passare a MongoDB 2.6 o versioni successive se non sei già lì per ottenere i migliori risultati.