github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/collection.go (about)

     1  // Copyright 2012-2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/mgo/v3"
     9  	"github.com/juju/mgo/v3/bson"
    10  
    11  	"github.com/juju/juju/mongo"
    12  )
    13  
    14  // modelStateCollection wraps a mongo.Collection, preserving the
    15  // mongo.Collection interface and its Writeable behaviour. It will
    16  // automatically modify query selectors and documents so that queries
    17  // and inserts only interact with data for a single model (where
    18  // possible).
    19  type modelStateCollection struct {
    20  	mongo.WriteCollection
    21  	modelUUID    string
    22  	queryTracker *queryTracker
    23  }
    24  
    25  // Writeable is part of the Collection interface.
    26  func (c *modelStateCollection) Writeable() mongo.WriteCollection {
    27  	// Note that we can't delegate this to the embedded WriteCollection:
    28  	// that would return a writeable collection without any env-handling.
    29  	return c
    30  }
    31  
    32  // Count returns the number of documents in the collection that belong
    33  // to the model that the modelStateCollection is filtering on.
    34  func (c *modelStateCollection) Count() (int, error) {
    35  	if c.queryTracker != nil {
    36  		c.queryTracker.TrackRead(c.Name(), "count")
    37  	}
    38  	return c.WriteCollection.Find(bson.D{{"model-uuid", c.modelUUID}}).Count()
    39  }
    40  
    41  // Find performs a query on the collection. The query must be given as
    42  // either nil or a bson.D.
    43  //
    44  // An "model-uuid" condition will always be added to the query to ensure
    45  // that only data for the model being filtered on is returned.
    46  //
    47  // If a simple "_id" field selector is included in the query
    48  // (e.g. "{{"_id", "foo"}}" the relevant model UUID prefix will
    49  // be added on to the id. Note that more complex selectors using the
    50  // "_id" field (e.g. using the $in operator) will not be modified. In
    51  // these cases it is up to the caller to add model UUID
    52  // prefixes when necessary.
    53  func (c *modelStateCollection) Find(query interface{}) mongo.Query {
    54  	if c.queryTracker != nil {
    55  		c.queryTracker.TrackRead(c.Name(), query)
    56  	}
    57  	return c.WriteCollection.Find(c.mungeQuery(query))
    58  }
    59  
    60  // FindId looks up a single document by _id. If the id is a string the
    61  // relevant model UUID prefix will be added to it. Otherwise, the
    62  // query will be handled as per Find().
    63  func (c *modelStateCollection) FindId(id interface{}) mongo.Query {
    64  	if c.queryTracker != nil {
    65  		c.queryTracker.TrackRead(c.Name(), bson.M{"_id": id})
    66  	}
    67  	if sid, ok := id.(string); ok {
    68  		return c.WriteCollection.FindId(ensureModelUUID(c.modelUUID, sid))
    69  	}
    70  	return c.Find(bson.D{{"_id", id}})
    71  }
    72  
    73  // Insert adds one or more documents to a collection. If the document
    74  // id is a string the model UUID prefix will be automatically
    75  // added to it. The model-uuid field will also be automatically added if
    76  // it is missing. An error will be returned if an model-uuid field is
    77  // provided but is the wrong value.
    78  func (c *modelStateCollection) Insert(docs ...interface{}) error {
    79  	var mungedDocs []interface{}
    80  	for _, doc := range docs {
    81  		mungedDoc, err := mungeDocForMultiModel(doc, c.modelUUID, modelUUIDRequired)
    82  		if err != nil {
    83  			return errors.Trace(err)
    84  		}
    85  		mungedDocs = append(mungedDocs, mungedDoc)
    86  	}
    87  	return c.WriteCollection.Insert(mungedDocs...)
    88  }
    89  
    90  // Update finds a single document matching the provided query document and
    91  // modifies it according to the update document.
    92  //
    93  // An "model-uuid" condition will always be added to the query to ensure
    94  // that only data for the model being filtered on is returned.
    95  //
    96  // If a simple "_id" field selector is included in the query
    97  // (e.g. "{{"_id", "foo"}}" the relevant model UUID prefix will
    98  // be added on to the id. Note that more complex selectors using the
    99  // "_id" field (e.g. using the $in operator) will not be modified. In
   100  // these cases it is up to the caller to add model UUID
   101  // prefixes when necessary.
   102  func (c *modelStateCollection) Update(query interface{}, update interface{}) error {
   103  	return c.WriteCollection.Update(c.mungeQuery(query), update)
   104  }
   105  
   106  // UpdateId finds a single document by _id and modifies it according to the
   107  // update document. The id must be a string or bson.ObjectId. The model
   108  // UUID will be automatically prefixed on to the id if it's a string and the
   109  // prefix isn't there already.
   110  func (c *modelStateCollection) UpdateId(id interface{}, update interface{}) error {
   111  	if sid, ok := id.(string); ok {
   112  		return c.WriteCollection.UpdateId(ensureModelUUID(c.modelUUID, sid), update)
   113  	}
   114  	return c.WriteCollection.UpdateId(bson.D{{"_id", id}}, update)
   115  }
   116  
   117  // Remove deletes a single document using the query provided. The
   118  // query will be handled as per Find().
   119  func (c *modelStateCollection) Remove(query interface{}) error {
   120  	return c.WriteCollection.Remove(c.mungeQuery(query))
   121  }
   122  
   123  // RemoveId deletes a single document by id. If the id is a string the
   124  // relevant model UUID prefix will be added on to it. Otherwise, the
   125  // query will be handled as per Find().
   126  func (c *modelStateCollection) RemoveId(id interface{}) error {
   127  	if sid, ok := id.(string); ok {
   128  		return c.WriteCollection.RemoveId(ensureModelUUID(c.modelUUID, sid))
   129  	}
   130  	return c.Remove(bson.D{{"_id", id}})
   131  }
   132  
   133  // RemoveAll deletes all documents that match a query. The query will
   134  // be handled as per Find().
   135  func (c *modelStateCollection) RemoveAll(query interface{}) (*mgo.ChangeInfo, error) {
   136  	return c.WriteCollection.RemoveAll(c.mungeQuery(query))
   137  }
   138  
   139  func (c *modelStateCollection) mungeQuery(inq interface{}) bson.D {
   140  	outq, err := mungeDocForMultiModel(inq, c.modelUUID, modelUUIDRequired|noModelUUIDInInput)
   141  	if err != nil {
   142  		panic(err)
   143  	}
   144  	return outq
   145  }