sql >> Database >  >> NoSQL >> MongoDB

Query en invoegen met een enkele opdracht

De zoekopdracht is niet zo ingewikkeld als hij op het eerste gezicht lijkt - de zoekopdracht om alle documenten te vinden die het bereik dat u krijgt "overlappen", is:

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

Dit komt overeen met elk document met een begindatum eerder dan onze einddatum en een einddatum die groter is dan onze begintijd. Als u de bereiken visualiseert als punten op een lijn:

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

de sX-eX-paren vertegenwoordigen bestaande bereiken. Als u een nieuwe s5-e5 neemt, kunt u zien dat als we paren elimineren die na beginnen onze einddatum (ze kunnen ons niet overlappen) en dan elimineren we alle paren die eindigen voor onze startdatum, als we niets meer hebben, dan zijn we goed om in te voegen.

Die voorwaarde zou zijn een unie van alle documenten met einddatum $lte onze start en die met startdatum $gte die van ons bevatten alle documenten die al in collectie zijn. Onze vraag draait dit om om er zeker van te zijn dat geen enkel document aan het tegenovergestelde van deze voorwaarde voldoet.

Op het gebied van prestaties is het jammer dat u uw datums alleen als tekenreeksen opslaat. Als u ze als tijdstempels (of een willekeurig nummer eigenlijk) hebt opgeslagen, kunt u ervoor zorgen dat deze zoekopdracht indexen beter gebruikt. Zoals het is, zou je voor prestaties een index willen hebben op { "startTime":1, "endTime":1 } .

Het is eenvoudig om te zien of het bereik dat u wilt invoegen bestaande bereiken overlapt, maar op uw tweede vraag:

Er is geen juiste manier om dit te doen met een invoegtoepassing, omdat ze geen zoekopdracht aannemen (d.w.z. ze zijn niet voorwaardelijk).

U kunt echter een update met upsert-voorwaarde gebruiken. Het kan invoegen als de voorwaarde nergens mee overeenkomt, maar als het wel overeenkomt, zal het proberen het overeenkomende document bij te werken!

Dus de truc die je zou gebruiken is om van de update een noop te maken en de velden die je nodig hebt alleen op upsert in te stellen. Sinds 2.4 is er een $setOnInsert exploitant om te updaten. Het volledige ding zou er ongeveer zo uitzien:

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 })

Ik heb net twee keer dezelfde "update" gedaan - de eerste keer waren er geen overlappende documenten, dus de update voerde een "upsert" uit die je kunt zien in de WriteResult het keerde terug.

Toen ik het een tweede keer uitvoerde, overlapte het (zelf natuurlijk) dus het probeerde het overeenkomende document bij te werken, maar merkte dat er geen werk te doen was. U kunt zien dat de geretourneerde nMatched 1 is, maar er is niets ingevoegd of gewijzigd.



  1. Som van subdocumenten in Mongoose

  2. PyMongo-zelfstudie:MongoDB-failover testen in uw Python-app

  3. Mongo-interface

  4. Hoe redis-server draaiende te houden?