MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Finden von zwei Dokumenten in MongoDB, die einen gemeinsamen Schlüsselwert haben

Während ich zu Kommentaren bereit stehe, dass ich nicht glaube, dass die Art und Weise, wie Sie Ihre Frage formulieren, tatsächlich mit einem bestimmten Problem zusammenhängt, das Sie haben, werde ich irgendwie den idiomatischen SQL-Weg in einer MongoDB-Lösung erklären. Ich stehe darauf, dass Ihre tatsächliche Lösung anders wäre, aber Sie haben uns dieses Problem nicht präsentiert, sondern nur SQL.

Betrachten Sie also die folgenden Dokumente als Beispielsatz und entfernen Sie _id-Felder in dieser Auflistung aus Gründen der Übersichtlichkeit:

{ "name" : "a", "type" : "b" }
{ "name" : "a", "type" : "c" }
{ "name" : "b", "type" : "c" }
{ "name" : "b", "type" : "a" }
{ "name" : "a", "type" : "b" }
{ "name" : "b", "type" : "c" }
{ "name" : "f", "type" : "e" }
{ "name" : "z", "type" : "z" }
{ "name" : "z", "type" : "z" }

Wenn wir das dargestellte SQL über dieselben Daten laufen lassen würden, würden wir dieses Ergebnis erhalten:

a|b
a|c
a|c
b|c
b|a
b|a
a|b
b|c

Wir können sehen, dass 2 Dokumente nicht übereinstimmen, und dann die Logik der SQL-Operation ausarbeiten. Die andere Art zu sagen lautet also:"Welche Dokumente mit einem Schlüssel "Name" tun mehr als einen haben möglicher Wert im Schlüssel "type".

In Anbetracht dessen, dass wir mit einem Mongo-Ansatz nach den Elementen suchen können, die nicht sind der gegebenen Bedingung entsprechen. Also quasi das Umgekehrte des Ergebnisses:

db.sample.aggregate([

    // Store unique documents grouped by the "name"
    {$group: { 
        _id: "$name",
        comp: {
            $addToSet: { 
                name:"$name",
                type: "$type" 
            }
        } 
    }},

    // Unwind the "set" results
    {$unwind: "$comp"},

    // Push the results back to get the unique count
    // *note* you could not have done this with alongside $addtoSet
    {$group: {
        _id: "$_id",
        comp: {
            $push: { 
                name: "$comp.name",
                type: "$comp.type" 
            }
        },
        count: {$sum: 1} 
    }},

    // Match only what was counted once
    {$match: {count: 1}},

    // Unwind the array
    {$unwind: "$comp"},

    // Clean up to "name" and "type" only
    {$project: { _id: 0, name: "$comp.name", type: "$comp.type"}}

])

Diese Operation liefert die Ergebnisse:

{ "name" : "f", "type" : "e" }
{ "name" : "z", "type" : "z" }

Um jetzt dasselbe Ergebnis wie die SQL-Abfrage zu erhalten, würden wir diese Ergebnisse nehmen und sie in eine andere Abfrage leiten:

db.sample.find({$nor: [{ name: "f", type: "e"},{ name: "z", type: "z"}] })

Was als endgültiges Übereinstimmungsergebnis ankommt:

{ "name" : "a", "type" : "b" }
{ "name" : "a", "type" : "c" }
{ "name" : "b", "type" : "c" }
{ "name" : "b", "type" : "a" }
{ "name" : "a", "type" : "b" }
{ "name" : "b", "type" : "c" }

Das wird also funktionieren, aber das einzige, was dies unpraktisch machen könnte, ist die Anzahl der Dokumente, die verglichen werden sehr groß ist, stoßen wir beim Komprimieren dieser Ergebnisse auf ein Array an eine Arbeitsgrenze.

Es leidet auch ein wenig unter der Verwendung eines Negativs in der letzten Suchoperation, die einen Scan der Sammlung erzwingen würde. Aber fairerweise könnte man dasselbe über die SQL-Abfrage sagen, die dasselbe negativ verwendet Prämisse.

Bearbeiten

Was ich natürlich nicht erwähnt habe, ist, dass, wenn die Ergebnismenge umgekehrt ist und Sie mehr abgleichen führt zu den ausgeschlossenen Elementen aus dem Aggregat, dann kehren Sie einfach die Logik um, um die gewünschten Schlüssel zu erhalten. Ändern Sie einfach $match wie folgt:

{$match: {$gt: 1}}

Und das wird das Ergebnis sein, vielleicht nicht die eigentlichen Dokumente, aber es ist ein Ergebnis. Sie brauchen also keine weitere Abfrage, um die negativen Fälle abzugleichen.

Und letztendlich war das meine Schuld, weil ich mich so auf die idiomatische Übersetzung konzentrierte, dass ich nicht las die letzte Zeile in Ihrer Frage, was zu tun ist Sagen Sie, dass Sie nach einem gesucht haben Dokument.

Natürlich aktuell Wenn diese Ergebnisgröße größer als 16 MB ist, stecken Sie fest. Zumindest bis zum 2.6 release, wo die Ergebnisse von Aggregationsoperationen ein Cursor , also können Sie das wie ein .find() iterieren .

Auch eingeführt in 2.6 ist der $size Operator, der verwendet wird, um die Größe eines Arrays im Dokument zu ermitteln. Das würde also helfen, das zweite $unwind zu entfernen und $group die verwendet werden, um die Länge des Satzes zu erhalten. Dies ändert die Abfrage in eine schnellere Form:

db.sample.aggregate([
    {$group: { 
        _id: "$name",
        comp: {
            $addToSet: { 
                name:"$name",
                type: "$type"
            }
        } 
    }},
    {$project: { 
        comp: 1,
        count: {$size: "$comp"} 
    }},
    {$match: {count: {$gt: 1}}},
    {$unwind: "$comp"},
    {$project: { _id: 0, name: "$comp.name", type: "$comp.type"}}
])

Und MongoDB 2.6.0-rc0 ist derzeit verfügbar, wenn Sie dies nur für den persönlichen Gebrauch oder zum Entwickeln/Testen tun.

Moral der Geschichte. Ja, können Sie Mach es, Aber tust du wirklich wollen oder brauchen es so zu machen? Dann wahrscheinlich nicht, und wenn Sie eine andere Frage zu dem spezifischen Geschäftsfall gestellt haben, erhalten Sie möglicherweise eine andere Antwort. Aber andererseits könnte dies genau das Richtige für das sein, was Sie wollen.

Hinweis

Erwähnenswert ist, dass, wenn Sie sich die Ergebnisse aus der SQL ansehen, diese fälschlicherweise dupliziert werden mehrere Elemente aufgrund der anderen verfügbaren Typoptionen, wenn Sie keinen DISTINCT verwendet haben für diese Werte oder im Wesentlichen eine andere Gruppierung. Aber das ist das Ergebnis, das von diesem Prozess mit MongoDB produziert wurde.

Für Alexander

Dies ist die Ausgabe des Aggregats in der Shell von aktuellen 2.4.x-Versionen:

{
    "result" : [
            {
                    "name" : "f",
                    "type" : "e"
            },
            {
                    "name" : "z",
                    "type" : "z"
            }
    ],
    "ok" : 1
}

Tun Sie dies also, um eine var zu erhalten, die als Argument an die $nor-Bedingung in der zweiten Suche übergeben wird, wie folgt:

var cond = db.sample.aggregate([ .....

db.sample.find({$nor: cond.result })

Und Sie sollten die gleichen Ergebnisse erzielen. Wenden Sie sich andernfalls an Ihren Fahrer.