Ci sono un paio di modi per farlo usando il framework di aggregazione
Solo un semplice insieme di dati per esempio:
{
"_id" : ObjectId("538181738d6bd23253654690"),
"movies": [
{ "_id": 1, "rating": 5 },
{ "_id": 2, "rating": 6 },
{ "_id": 3, "rating": 7 }
]
},
{
"_id" : ObjectId("538181738d6bd23253654691"),
"movies": [
{ "_id": 1, "rating": 5 },
{ "_id": 4, "rating": 6 },
{ "_id": 2, "rating": 7 }
]
},
{
"_id" : ObjectId("538181738d6bd23253654692"),
"movies": [
{ "_id": 2, "rating": 5 },
{ "_id": 5, "rating": 6 },
{ "_id": 6, "rating": 7 }
]
}
Usando il primo "utente" come esempio, ora vuoi scoprire se qualcuno degli altri due utenti ha almeno due degli stessi film.
Per MongoDB 2.6 e versioni successive puoi semplicemente utilizzare $setIntersection
operatore insieme a $size
operatore:
db.users.aggregate([
// Match the possible documents to reduce the working set
{ "$match": {
"_id": { "$ne": ObjectId("538181738d6bd23253654690") },
"movies._id": { "$in": [ 1, 2, 3 ] },
"$and": [
{ "movies": { "$not": { "$size": 1 } } }
]
}},
// Project a copy of the document if you want to keep more than `_id`
{ "$project": {
"_id": {
"_id": "$_id",
"movies": "$movies"
},
"movies": 1,
}},
// Unwind the array
{ "$unwind": "$movies" },
// Build the array back with just `_id` values
{ "$group": {
"_id": "$_id",
"movies": { "$push": "$movies._id" }
}},
// Find the "set intersection" of the two arrays
{ "$project": {
"movies": {
"$size": {
"$setIntersection": [
[ 1, 2, 3 ],
"$movies"
]
}
}
}},
// Filter the results to those that actually match
{ "$match": { "movies": { "$gte": 2 } } }
])
Ciò è ancora possibile nelle versioni precedenti di MongoDB che non dispongono di tali operatori, utilizzando solo alcuni passaggi in più:
db.users.aggregate([
// Match the possible documents to reduce the working set
{ "$match": {
"_id": { "$ne": ObjectId("538181738d6bd23253654690") },
"movies._id": { "$in": [ 1, 2, 3 ] },
"$and": [
{ "movies": { "$not": { "$size": 1 } } }
]
}},
// Project a copy of the document along with the "set" to match
{ "$project": {
"_id": {
"_id": "$_id",
"movies": "$movies"
},
"movies": 1,
"set": { "$cond": [ 1, [ 1, 2, 3 ], 0 ] }
}},
// Unwind both those arrays
{ "$unwind": "$movies" },
{ "$unwind": "$set" },
// Group back the count where both `_id` values are equal
{ "$group": {
"_id": "$_id",
"movies": {
"$sum": {
"$cond":[
{ "$eq": [ "$movies._id", "$set" ] },
1,
0
]
}
}
}},
// Filter the results to those that actually match
{ "$match": { "movies": { "$gte": 2 } } }
])
In dettaglio
Potrebbe essere un po' difficile da comprendere, quindi possiamo dare un'occhiata a ogni fase e suddividerle per vedere cosa stanno facendo.
$corrispondenza :Non vuoi operare su tutti i documenti della collezione, quindi questa è un'opportunità per rimuovere gli elementi che non corrispondono eventualmente anche se c'è ancora più lavoro da fare per trovare l'esatto quelli. Quindi le cose più ovvie sono escludere lo stesso "utente" e quindi abbinare solo i documenti che hanno almeno uno degli stessi filmati trovati per quell'"utente".
La prossima cosa che ha senso è considerarlo quando vuoi abbinare n
voci quindi solo i documenti che hanno un array "film" che è più grande di n-1
può effettivamente contenere corrispondenze. L'uso di $and
qui sembra divertente e non è richiesto in modo specifico, ma se le corrispondenze richieste fossero 4
quindi quella parte effettiva della dichiarazione sarebbe simile a questa:
"$and": [
{ "movies": { "$not": { "$size": 1 } } },
{ "movies": { "$not": { "$size": 2 } } },
{ "movies": { "$not": { "$size": 3 } } }
]
Quindi in pratica "escludi" array che non sono probabilmente abbastanza lunghi da avere n
partite. Notando qui che questo $size
l'operatore nel modulo di query è diverso da $size
per il quadro di aggregazione. Non c'è modo, ad esempio, di usarlo con un operatore di disuguaglianza come $gt
il suo scopo è quello di corrispondere in modo specifico alla "taglia" richiesta. Quindi questo modulo di query per specificare tutte le dimensioni possibili che sono inferiori a.
$progetto :Ci sono alcuni scopi in questa affermazione, alcuni dei quali differiscono a seconda della versione di MongoDB che hai. In primo luogo, e facoltativamente, una copia del documento viene conservata sotto il _id
valore in modo che questi campi non vengano modificati dal resto dei passaggi. L'altra parte qui è mantenere l'array "film" nella parte superiore del documento come copia per la fase successiva.
Quello che sta succedendo anche nella versione presentata per le versioni precedenti alla 2.6 è che c'è un array aggiuntivo che rappresenta il _id
valori per i "film" da abbinare. L'utilizzo di $cond
operatore qui è solo un modo per creare una rappresentazione "letterale" dell'array. Abbastanza divertente, MongoDB 2.6 introduce un operatore noto come $literal
per fare esattamente questo senza il modo divertente in cui stiamo usando $cond
proprio qui.
$rilassati :Per fare qualsiasi altra cosa, l'array movies deve essere svolto poiché in entrambi i casi è l'unico modo per isolare il _id
esistente valori per le voci che devono essere confrontate con il "set". Quindi per la versione precedente alla 2.6 è necessario "rilassare" entrambi gli array presenti.
$gruppo :Per MongoDB 2.6 e versioni successive stai semplicemente raggruppando in un array che contiene solo _id
valori dei film con i "rating" rimossi.
Pre 2.6 poiché tutti i valori sono presentati "affiancati" (e con molte duplicazioni) stai facendo un confronto tra i due valori per vedere se sono gli stessi. Dove è true
, questo dice a $cond
operatore per restituire un valore di 1
o 0
dove la condizione è false
. Questo viene ritrasmesso direttamente tramite $sum
per sommare il numero di elementi corrispondenti nell'array al "set" richiesto.
$progetto :Dove questa è la parte diversa per MongoDB 2.6 e versioni successive è che dal momento che hai respinto un array di "film" _id
valori che stai quindi utilizzando $setIntersection
per confrontare direttamente quegli array. Come risultato di ciò è un array contenente gli elementi che sono gli stessi, questo viene quindi racchiuso in un $size
per determinare quanti elementi sono stati restituiti in quel set corrispondente.
$corrispondenza :è la fase finale che è stata implementata qui che fa il passo chiaro di abbinare solo quei documenti il cui numero di elementi intersecanti era maggiore o uguale al numero richiesto.
Finale
Fondamentalmente è così che lo fai. Prima della 2.6 è un po' più ingombrante e richiederà un po' più di memoria a causa dell'espansione che viene eseguita duplicando ogni membro dell'array trovato da tutti i possibili valori del set, ma è comunque un modo valido per farlo.
Tutto quello che devi fare è applicarlo con il n
maggiore valori corrispondenti per soddisfare le tue condizioni e, naturalmente, assicurati che la tua corrispondenza utente originale abbia il n
richiesto possibilità. Altrimenti basta generarlo su n-1
dalla lunghezza dell'array di "film" dell'"utente".