github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	"gopkg.in/mgo.v2"
     8  	"gopkg.in/mgo.v2/bson"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/juju/mongo"
    12  )
    13  
    14  // getCollection fetches a named collection using a new session if the
    15  // database has previously been logged in to. It returns the
    16  // collection and a closer function for the session.
    17  //
    18  // If the collection stores documents for multiple models, the
    19  // returned collection will automatically perform model
    20  // filtering where possible. See modelStateCollection below.
    21  func (st *State) getCollection(name string) (mongo.Collection, func()) {
    22  	return st.database.GetCollection(name)
    23  }
    24  
    25  // getRawCollection returns the named mgo Collection. As no automatic
    26  // model filtering is performed by the returned collection it
    27  // should be rarely used. getCollection() should be used in almost all
    28  // cases.
    29  func (st *State) getRawCollection(name string) (*mgo.Collection, func()) {
    30  	collection, closer := st.database.GetCollection(name)
    31  	return collection.Writeable().Underlying(), closer
    32  }
    33  
    34  // modelStateCollection wraps a mongo.Collection, preserving the
    35  // mongo.Collection interface and its Writeable behaviour. It will
    36  // automatically modify query selectors and documents so that queries
    37  // and inserts only interact with data for a single model (where
    38  // possible).
    39  type modelStateCollection struct {
    40  	mongo.WriteCollection
    41  	modelUUID string
    42  }
    43  
    44  // Writeable is part of the Collection interface.
    45  func (c *modelStateCollection) Writeable() mongo.WriteCollection {
    46  	// Note that we can't delegate this to the embedded WriteCollection:
    47  	// that would return a writeable collection without any env-handling.
    48  	return c
    49  }
    50  
    51  // Count returns the number of documents in the collection that belong
    52  // to the model that the modelStateCollection is filtering on.
    53  func (c *modelStateCollection) Count() (int, error) {
    54  	return c.WriteCollection.Find(bson.D{{"model-uuid", c.modelUUID}}).Count()
    55  }
    56  
    57  // Find performs a query on the collection. The query must be given as
    58  // either nil or a bson.D.
    59  //
    60  // An "model-uuid" condition will always be added to the query to ensure
    61  // that only data for the model being filtered on is returned.
    62  //
    63  // If a simple "_id" field selector is included in the query
    64  // (e.g. "{{"_id", "foo"}}" the relevant model UUID prefix will
    65  // be added on to the id. Note that more complex selectors using the
    66  // "_id" field (e.g. using the $in operator) will not be modified. In
    67  // these cases it is up to the caller to add model UUID
    68  // prefixes when necessary.
    69  func (c *modelStateCollection) Find(query interface{}) mongo.Query {
    70  	return c.WriteCollection.Find(c.mungeQuery(query))
    71  }
    72  
    73  // FindId looks up a single document by _id. If the id is a string the
    74  // relevant model UUID prefix will be added to it. Otherwise, the
    75  // query will be handled as per Find().
    76  func (c *modelStateCollection) FindId(id interface{}) mongo.Query {
    77  	if sid, ok := id.(string); ok {
    78  		return c.WriteCollection.FindId(ensureModelUUID(c.modelUUID, sid))
    79  	}
    80  	return c.Find(bson.D{{"_id", id}})
    81  }
    82  
    83  // Insert adds one or more documents to a collection. If the document
    84  // id is a string the model UUID prefix will be automatically
    85  // added to it. The model-uuid field will also be automatically added if
    86  // it is missing. An error will be returned if an model-uuid field is
    87  // provided but is the wrong value.
    88  func (c *modelStateCollection) Insert(docs ...interface{}) error {
    89  	var mungedDocs []interface{}
    90  	for _, doc := range docs {
    91  		mungedDoc, err := mungeDocForMultiEnv(doc, c.modelUUID, modelUUIDRequired)
    92  		if err != nil {
    93  			return errors.Trace(err)
    94  		}
    95  		mungedDocs = append(mungedDocs, mungedDoc)
    96  	}
    97  	return c.WriteCollection.Insert(mungedDocs...)
    98  }
    99  
   100  // Update finds a single document matching the provided query document and
   101  // modifies it according to the update document.
   102  //
   103  // An "model-uuid" condition will always be added to the query to ensure
   104  // that only data for the model being filtered on is returned.
   105  //
   106  // If a simple "_id" field selector is included in the query
   107  // (e.g. "{{"_id", "foo"}}" the relevant model UUID prefix will
   108  // be added on to the id. Note that more complex selectors using the
   109  // "_id" field (e.g. using the $in operator) will not be modified. In
   110  // these cases it is up to the caller to add model UUID
   111  // prefixes when necessary.
   112  func (c *modelStateCollection) Update(query interface{}, update interface{}) error {
   113  	return c.WriteCollection.Update(c.mungeQuery(query), update)
   114  }
   115  
   116  // UpdateId finds a single document by _id and modifies it according to the
   117  // update document. The id must be a string or bson.ObjectId. The model
   118  // UUID will be automatically prefixed on to the id if it's a string and the
   119  // prefix isn't there already.
   120  func (c *modelStateCollection) UpdateId(id interface{}, update interface{}) error {
   121  	if sid, ok := id.(string); ok {
   122  		return c.WriteCollection.UpdateId(ensureModelUUID(c.modelUUID, sid), update)
   123  	}
   124  	return c.WriteCollection.UpdateId(bson.D{{"_id", id}}, update)
   125  }
   126  
   127  // Remove deletes a single document using the query provided. The
   128  // query will be handled as per Find().
   129  func (c *modelStateCollection) Remove(query interface{}) error {
   130  	return c.WriteCollection.Remove(c.mungeQuery(query))
   131  }
   132  
   133  // RemoveId deletes a single document by id. If the id is a string the
   134  // relevant model UUID prefix will be added on to it. Otherwise, the
   135  // query will be handled as per Find().
   136  func (c *modelStateCollection) RemoveId(id interface{}) error {
   137  	if sid, ok := id.(string); ok {
   138  		return c.WriteCollection.RemoveId(ensureModelUUID(c.modelUUID, sid))
   139  	}
   140  	return c.Remove(bson.D{{"_id", id}})
   141  }
   142  
   143  // RemoveAll deletes all documents that match a query. The query will
   144  // be handled as per Find().
   145  func (c *modelStateCollection) RemoveAll(query interface{}) (*mgo.ChangeInfo, error) {
   146  	return c.WriteCollection.RemoveAll(c.mungeQuery(query))
   147  }
   148  
   149  func (c *modelStateCollection) mungeQuery(inq interface{}) bson.D {
   150  	outq, err := mungeDocForMultiEnv(inq, c.modelUUID, modelUUIDRequired|noModelUUIDInInput)
   151  	if err != nil {
   152  		panic(err)
   153  	}
   154  	return outq
   155  }