3838
3939
4040def elem_match_pred (value , query , options ):
41- if type (value ) is list :
41+ if isinstance (value , list ) :
4242 if False not in [k .startswith ("$" ) for k in query ["$elemMatch" ]]:
4343 for i in value :
4444 okay = True
@@ -76,6 +76,7 @@ def elem_match_pred(value, query, options):
7676 return False
7777
7878
79+ # Assumes the `document` passed in is a type of, or subtype of `OrderedDict`
7980def expand (field , document , check_last_array , expand_array , add_last_array , check_none_at_all , check_none_next , debug ):
8081 ret = []
8182
@@ -150,9 +151,9 @@ def evaluate(field, query, document, options, debug=False):
150151 if type (query ) != dict :
151152
152153 def pred (value , query , options ):
153- if type (value ) == OrderedDict :
154+ if isinstance (value , OrderedDict ) :
154155 value = dict (value )
155- if type ( query ) == OrderedDict :
156+ if isinstance ( value , OrderedDict ) :
156157 query = dict (query )
157158 return value == query
158159 elif '$ne' in query :
@@ -355,13 +356,13 @@ def include_in_projection(projection, k, v, node):
355356 return node .sub (k ).included or (not is_simple (v ) and node .sub (k ).fields )
356357
357358
358- def project (document , projection_fields ):
359+ def project (documents , projection_fields ):
359360 projection = Projection (projection_fields )
360361 projected_results = []
361- for values in document :
362+ for document in documents :
362363 results = {}
363- # print "Projecting %r with %r" % (values , projection_fields)
364- for k , v in values .items ():
364+ # print "Projecting %r with %r" % (document , projection_fields)
365+ for k , v in document .items ():
365366 # print "Checking %r => %r with %s" % (k, v, projection.root)
366367 if k == '_id' :
367368 if projection .include_id :
@@ -431,14 +432,13 @@ def getTypeCode(value):
431432 return "20"
432433 elif isinstance (value , (long , float , int )):
433434 return "30"
435+ elif isinstance (value , binary .Binary ):
436+ # this needs to come before basestring because `bson.binary.Binary` is also a subtype of `basestring`
437+ return "70"
434438 elif isinstance (value , basestring ):
435439 return "40"
436440 elif isinstance (value , OrderedDict ):
437441 return "51"
438- elif isinstance (value , (list , tuple )):
439- return "60"
440- elif isinstance (value , binary .Binary ):
441- return "70"
442442 elif isinstance (value , ObjectId ):
443443 return "80"
444444 elif isinstance (value , datetime .datetime ):
@@ -455,6 +455,7 @@ def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
455455 items = super (SortedDict , self ).items ()
456456 # group them together first
457457 tmp = OrderedDict ()
458+ tmp ["20" ] = []
458459 tmp ["30" ] = []
459460 tmp ["40" ] = []
460461 tmp ["51" ] = []
@@ -469,6 +470,8 @@ def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
469470 for typeCode , kvs in tmp .items ():
470471 if typeCode == "51" :
471472 sortedItems .extend (sorted (kvs , key = lambda kv : bson .BSON .encode (SortedDict .fromOrderedDict (kv [0 ]))))
473+ elif typeCode == "70" :
474+ sortedItems .extend (sorted (kvs , key = lambda kv : str (len (kv [0 ][:- 1 ])) + str (kv [0 ].subtype ) + kv [0 ][:- 1 ]))
472475 else :
473476 sortedItems .extend (sorted (kvs ))
474477 # print str(sortedItems)
@@ -525,13 +528,13 @@ def _insert(self, doc):
525528 def insert (self , input ):
526529 oldData = deepcopy (self .data )
527530 try :
528- if type (input ) is OrderedDict :
531+ if isinstance (input , OrderedDict ) :
529532 self ._insert (input )
530- elif type (input ) is list :
533+ elif isinstance (input , list ) :
531534 all_ids = set ()
532535 for i in input :
533536 if '_id' in i :
534- if not self .options .object_field_order_matters and type (i ['_id' ]) is HashableOrderedDict :
537+ if not self .options .object_field_order_matters and isinstance (i ['_id' ], HashableOrderedDict ) :
535538 i ['_id' ] = HashableOrderedDict (sorted (i ['_id' ].items (), key = lambda (key , value ): key ))
536539 if i ['_id' ] in all_ids :
537540 # print i['_id']
@@ -831,8 +834,18 @@ def process_update_operator_pull_all(self, key, update_expression):
831834 raise MongoModelException ("Cannot apply $pullAll to a non-array field." , code = 10142 )
832835
833836 def evaluate (self , query , document ):
834- field = query .keys ()[0 ]
835- return evaluate (field , query [field ], document , self .options , True )
837+ if len (query ) == 0 :
838+ return True # match empty query, since coll.find({}) returns all docs
839+ acc = True
840+ for field in query .keys ():
841+ if field == '_id' :
842+ tmp = OrderedDict ()
843+ for k ,v in sorted (query [field ].items (), key = lambda i : i [0 ]):
844+ tmp [k ] = v
845+ acc = acc and evaluate (field , tmp , document , self .options , True )
846+ else :
847+ acc = acc and evaluate (field , query [field ], document , self .options , True )
848+ return acc
836849
837850 def process_update_operator_pull (self , key , update_expression ):
838851 # print "Update Operator: $pull ", update
@@ -848,10 +861,10 @@ def process_update_operator_pull(self, key, update_expression):
848861 if self .evaluate (v , item ):
849862 # print "item match0!, remove: ", item
850863 self .data [key ][k ].remove (item )
851- elif isinstance (v , list ):
852- if item in v :
853- # print "item match1!, remove: ", item
854- self .data [key ][k ].remove (item )
864+ elif isinstance (v , list ):
865+ if item in v :
866+ # print "item match1!, remove: ", item
867+ self .data [key ][k ].remove (item )
855868 else :
856869 self .data [key ][k ] = [x for x in self .data [key ][k ] if x != v ]
857870 else :
@@ -956,9 +969,6 @@ def process_update_operator(self, key, update, new_doc=False):
956969 self .mapUpdateOperator [k ](key , update [k ])
957970 else :
958971 self .replace (key , update )
959- for index in self .indexes :
960- if not index .inError :
961- index .validate_and_build_entry (self .data .values ())
962972 except MongoModelException as e :
963973 self .data [key ] = old_data
964974 raise e
@@ -1136,6 +1146,7 @@ def update(self, query, update, upsert, multi):
11361146 if not index .inError :
11371147 index .validate_and_build_entry (self .data .values ())
11381148 except MongoModelException as e :
1149+ # print "*********************** Reseting update due to {}".format(e)
11391150 self .data = old_data
11401151 raise e
11411152
@@ -1225,11 +1236,11 @@ def validate_self(self):
12251236 raise MongoModelException ("No index name specified" , code = 29967 )
12261237
12271238 def build (self , documents ):
1228- self .validate_and_build_entry (documents )
1239+ self .validate_and_build_entry (documents , first_build = True )
12291240
12301241 # Validate the entry(ies) that will be built on this particular document for the following invariants:
12311242 # - If it's compund index, the cartesian product of all index values cannot exceed 1000
1232- def validate_and_build_entry (self , documents ):
1243+ def validate_and_build_entry (self , documents , first_build = False ):
12331244 for document in documents :
12341245 nValues = 1
12351246 for key in self .keys :
@@ -1244,20 +1255,20 @@ def validate_and_build_entry(self, documents):
12441255 debug = False )
12451256 nValues * len (values )
12461257 if nValues > 1000 and not self .isSimple :
1247- self .inError = True
1258+ self .inError = first_build
12481259 raise MongoModelException ("Multi-multikey index size exceeds maximum value." , code = 0 )
12491260
12501261class MongoUniqueIndex (MongoIndex ):
12511262 def __init__ (self , indexKeys , kwargs ):
12521263 super (MongoUniqueIndex , self ).__init__ (indexKeys , kwargs )
12531264
12541265 def build (self , documents ):
1255- super (MongoUniqueIndex , self ).validate_and_build_entry (documents )
1256- self .validate_and_build_entry (documents )
1266+ super (MongoUniqueIndex , self ).validate_and_build_entry (documents , first_build = True )
1267+ self .validate_and_build_entry (documents , first_build = True )
12571268
12581269 # Validate the entry(ies) that will be built on this particular document for the following invariants:
12591270 # - No duplicates
1260- def validate_and_build_entry (self , documents ):
1271+ def validate_and_build_entry (self , documents , first_build = False ):
12611272 seen = set ()
12621273 for document in documents :
12631274 entry = ""
@@ -1279,7 +1290,7 @@ def validate_and_build_entry(self, documents):
12791290 # import pprint
12801291 # print "Violation doc"
12811292 # pprint.pprint(dict(document))
1282- self .inError = True
1293+ self .inError = first_build
12831294 raise MongoModelException ("Duplicated value not allowed by unique index" , code = 11000 )
12841295 else :
12851296 # print "Inserting {} for key {}".format(entry, key)
0 commit comments