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

Abfragen und Einfügen mit einem einzigen Befehl

Die Abfrage ist nicht so kompliziert, wie sie auf den ersten Blick aussehen mag - die Abfrage, um alle Dokumente zu finden, die den angegebenen Bereich "überlappen", lautet:

db.test.find( { "startTime" : { "$lt" : new_end_time }, 
                "endTime"   : { "$gt": new_start_time } 
            } 
)

Dies stimmt mit jedem Dokument überein, dessen Startdatum vor unserem Enddatum und dessen Enddatum vor unserer Startzeit liegt. Wenn Sie sich die Bereiche als Punkte auf einer Linie vorstellen:

-----|*********|----------|****|-----------|******||********|---
    s1         e1         s2   e2         s3     e3s4       e4

Die sX-eX-Paare repräsentieren bestehende Sortimente. Wenn Sie ein neues s5-e5 nehmen, können Sie das sehen, wenn wir Paare eliminieren, die nach beginnen unser Enddatum (sie können uns nicht überlappen) und dann eliminieren wir alle Paare, die vor unserem Startdatum enden, wenn wir nichts mehr haben, können wir gut einfügen.

Diese Bedingung wäre eine Vereinigung aller Dokumente mit dem Enddatum $lte unser Start und diejenigen mit Startdatum $gte unsere umfassen alle bereits in Sammlung befindlichen Dokumente. Unsere Abfrage dreht dies um, um sicherzustellen, dass keine Dokumente das Gegenteil dieser Bedingung erfüllen.

An der Leistungsfront ist es bedauerlich, dass Sie Ihre Daten nur als Zeichenfolgen speichern. Wenn Sie sie als Zeitstempel (oder wirklich eine beliebige Zahl) speichern, könnten Sie diese Abfrage dazu bringen, Indizes besser zu nutzen. Aus Leistungsgründen sollten Sie einen Index für { "startTime":1, "endTime":1 } haben .

Es ist einfach herauszufinden, ob der Bereich, den Sie einfügen möchten, vorhandene Bereiche überlappt, aber zu Ihrer zweiten Frage:

Es gibt keine richtige Möglichkeit, dies mit Einfügungen zu tun, da sie keine Abfrage annehmen (d. H. Sie sind nicht bedingt).

Sie können jedoch ein Update mit Upsert-Bedingung verwenden. Es kann einfügen, wenn die Bedingung nicht zutrifft, aber wenn es zutrifft, wird es versuchen, das übereinstimmende Dokument zu aktualisieren!

Der Trick, den Sie verwenden würden, besteht also darin, das Update zu einem Noop zu machen und die Felder festzulegen, die Sie nur beim Upsert benötigen. Seit 2.4 gibt es ein $setOnInsert Betreiber zu aktualisieren. Das Ganze würde in etwa so aussehen:

db.test.update( 
   { startTime: { "$lt" : new_end_time }, "endTime" : { "$gt": new_start_time } }, 
   { $setOnInsert:{ startTime:new_start_time, endTime: new_end_time}},
   {upsert:1}
)
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("538e0f6e7110dddea4383938")
})
db.test.update(
   { startTime:{ "$lt" : new_end_time }, "endTime" : { "$gt": new_start_time } },
   { $setOnInsert:{ startTime:new_start_time, endTime: new_end_time}},
   {upsert:1}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })

Ich habe gerade zweimal dasselbe "Update" durchgeführt - beim ersten Mal gab es keine überlappenden Dokumente, also führte das Update ein "Upsert" durch, das Sie im WriteResult sehen können es kam zurück.

Als ich es ein zweites Mal ausführte, überlappte es (natürlich sich selbst), also versuchte es, das übereinstimmende Dokument zu aktualisieren, bemerkte aber, dass es keine Arbeit zu tun gab. Sie können sehen, dass das zurückgegebene nMatched 1 ist, aber nichts eingefügt oder geändert wurde.