Changeset 120
- Timestamp:
- 12/08/05 11:30:24
- Files:
-
- trunk/arenas.py (modified) (3 diffs)
- trunk/doc/advanced.html (modified) (1 diff)
- trunk/doc/modeling.html (modified) (2 diffs)
- trunk/test/zoo_fixture.py (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/arenas.py
r119 r120 233 233 unit.sandbox = None 234 234 235 def xrecall(self, cl s, expr=None):235 def xrecall(self, classes, expr=None): 236 236 """Iterator over units of cls which match expr.""" 237 237 if classes.__class__.__name__ == "UnitJoin": 238 for unitrow in self.xmulti(classes, expr): 239 yield unitrow 240 return 241 242 cls = classes 238 243 self.arena.log("RECALL %s: %s" % (cls.__name__, expr), LOGRECALL) 239 244 … … 275 280 yield unit 276 281 277 def recall(self, cl s, expr=None):282 def recall(self, classes, expr=None): 278 283 """List of units of class 'cls' which match expr.""" 279 return [x for x in self.xrecall(cl s, expr)]280 281 def multirecall(self, classes, expr=None):282 """ multirecall(classes, expr) -> [[unit, ...], [unit, ...], ...]284 return [x for x in self.xrecall(classes, expr)] 285 286 def xmulti(self, classes, expr=None): 287 """xmulti(classes, expr) -> [[unit, ...], [unit, ...], ...] 283 288 Recall units of each cls if they together match the expr. 284 285 Units of each additional cls pair will be recalled; however, only286 those Units with associations to Units in the previous set will be287 returned. For you database guys, it's a set of inner joins, each of288 which is between each class and its direct antecedent in the list.289 289 290 290 Each yielded value will be a list of Units, in the same order as … … 292 292 code like: 293 293 294 for invoice, price in sandbox. multirecall([Invoice, Price], f):294 for invoice, price in sandbox.xmulti(Invoice & Price, f): 295 295 deal_with(invoice) 296 296 deal_with(price) trunk/doc/advanced.html
r119 r120 133 133 </li> 134 134 <li><tt>multirecall(self, classes, expr):</tt> Recommended. 135 The 'classes' argument will be a UnitJoin and its children. 135 136 This method must return an iterable of lists; each item in each list 136 will be a Unit. The Units must be of the supplied classes, in order, 137 and must all match Expression(*resultset) together. 137 will be a Unit. The Units must be of the supplied classes, in order 138 (see the UnitJoin.classes method), and must all match 139 expr(*resultset) together. 138 140 </li> 139 141 </ul> trunk/doc/modeling.html
r119 r120 323 323 cache.</p> 324 324 325 <h5>Recalling multiple classes at once (JOINs)</h5> 326 327 <p>In addition to providing a single class to <tt>recall</tt>, you have 328 the option of providing a tree of classes, a nested set of 329 <tt class='def'>UnitJoin(class1, class2, leftbiased=None)</tt> 330 instances.</p> 331 332 333 <p>The "leftbiased" argument specifies how the results will be 334 joined:</p> 335 336 <table> 337 <tr><th>leftbiased</th><th>Join Type</th><th>Description</th><th>Operator</th></tr> 338 <tr> 339 <td>None</td> 340 <td>Inner Join</td> 341 <td>All related pairs of both classes will be returned.</td> 342 <td><tt>&</tt> or <tt>+</tt></td> 343 </tr> 344 <tr> 345 <td>True</td> 346 <td>Left Join</td> 347 <td>All related pairs of both classes will be returned. In addition, 348 if any Unit in class1 has no match in class2, we return a single 349 row with Unit1 and a "null Unit" (a Unit, all of whose properties 350 are None).</td> 351 <td><tt><<</tt></td> 352 </tr> 353 <tr> 354 <td>False</td> 355 <td>Right Join</td> 356 <td>All related pairs of both classes will be returned. In addition, 357 if any Unit in class2 has no match in class1, we return a single 358 row with a "null Unit" (a Unit, all of whose properties are None) 359 and Unit2.</td> 360 <td><tt>>></tt></td> 361 </tr> 362 </table> 363 364 <p>Look hard? Fear not. There's a <b>much</b> easier way to join units than 365 writing a big tree of UnitJoins. Use the &, <<, and >> 366 operators directly with Unit classes:</p> 367 368 <pre>tree = (Book << Publisher) & Author</pre> 369 370 <p>This example will automatically produce a UnitJoin tree for you, 371 with Book 'left joined' to Publisher, and then 'inner joined' to 372 Author.</p> 373 374 <p>When you provide multiple classes, the <tt>recall</tt> method returns 375 a list of rows. Each row will be a list of units, one per class in the 376 <tt>classes</tt> arg. The <tt>expr</tt> arg should be a 377 <tt>logic.Expression</tt> which can evaluate all of the 378 units in any given row at once. 379 380 <pre>pubs = box.recall(Publisher & Book, 381 logic.Expression(lambda p, b: p.ID == 4))</pre> 382 383 This example will retrieve a series of [Publisher, Book] pairs. 384 Note that all three constructs (the UnitJoins, the lambda, and 385 the resulting rows) have the same classes listed in order from 386 left to right.</p> 387 388 <p>In database terminology, this technique performs a series of joins 389 between each pair of classes in your UnitJoin tree. However, repeated units 390 in the results will reference the same object; in the example above, each 391 Publisher unit will be the same object, since we limited that expression to 392 a single Publisher. So we might examine multiple rows in the "pubs" list, 393 but the first unit in each row will be the same unit instance.</p> 394 395 <p>The relationships (joins) between each class are specified by 396 Unit Associations (see <a href='#associations'>below</a>).</p> 397 325 398 <h5>xrecall()</h5> 326 399 <p>Just like recall, but returns an iterator instead of a list. Use xrecall … … 341 414 If multiple Units match the criteria, only the first one is returned 342 415 (although the rest are probably loaded into memory).</p> 343 344 <h5>multirecall()</h5>345 <p>The <tt class='def'>multirecall(classes, expr)</tt> method yields346 a series of unitsets. Each unitset will be a list of units, one per347 class in the <tt>classes</tt> arg. The <tt>expr</tt> arg should be a348 <tt>logic.Expression</tt> which can evaluate all of the units in349 any given unitset at once.350 <pre>pubs = box.multirecall((Publisher, Book),351 logic.Expression(lambda p, b: p.ID == 4))</pre>352 This example will retrieve a series of (Publisher, Book) pairs.</p>353 354 <p>In database terminology, the multirecall method performs a series of355 full inner joins between each class and its neighbor. That is, class 1356 is joined to class 2, then class 2 is joined to class 3, etcetera.357 Since each join is an inner join, the result set is guaranteed358 to contain a complete set of units for each iteration; however, repeated359 units will reference the same object; in the example above, each Publisher360 unit will be the same object, since we limited that expression to a single361 Publisher. So we might iterate over "pubs" multiple times, but each time,362 the first unit in the set will be the same unit instance.</p>363 364 <p>The relationships (joins) between each class are specified by365 Unit Associations (see <a href='#associations'>below</a>).</p>366 416 367 417 <h4>Forgetting and Repressing</h4> trunk/test/zoo_fixture.py
r119 r120 441 441 442 442 f = logic.Expression(lambda z, a: z.Name == 'San Diego Zoo') 443 zooed_animals = [(z, a) for z, a in 444 box.multirecall(Zoo & Animal, f)] 443 zooed_animals = box.recall(Zoo & Animal, f) 445 444 self.assertEqual(len(zooed_animals), 2) 446 445 … … 458 457 sdexpr = logic.filter(Name='San Diego Zoo') 459 458 leo = logic.Expression(lambda z, a: a.Species == 'Leopard') 460 zooed_animals = [(z, a) for z, a in 461 box.multirecall(Zoo & Animal, sdexpr + leo)] 459 zooed_animals = box.recall(Zoo & Animal, sdexpr + leo) 462 460 self.assertEqual(len(zooed_animals), 0) 463 461 464 462 # Now try the same expr with INNER, LEFT, and RIGHT JOINs. 465 zooed_animals = list(box.multirecall(Zoo & Animal))463 zooed_animals = box.recall(Zoo & Animal) 466 464 self.assertEqual(len(zooed_animals), 6) 467 465 self.assertEqual(set([(z.Name, a.Species) for z, a in zooed_animals]), … … 473 471 ("Sea_World", "Adelie Penguin")])) 474 472 475 zooed_animals = list(box.multirecall(Zoo >> Animal))473 zooed_animals = box.recall(Zoo >> Animal) 476 474 self.assertEqual(len(zooed_animals), 12) 477 475 self.assertEqual(set([(z.Name, a.Species) for z, a in zooed_animals]), … … 490 488 ])) 491 489 492 zooed_animals = list(box.multirecall(Zoo << Animal))490 zooed_animals = box.recall(Zoo << Animal) 493 491 self.assertEqual(len(zooed_animals), 7) 494 492 self.assertEqual(set([(z.Name, a.Species) for z, a in zooed_animals]), … … 504 502 # Try a multiple-arg expression 505 503 f = logic.Expression(lambda a, z: a.Legs >= 4 and z.Admission < 10) 506 animal_zoos = [(a, z) for a, z in box.multirecall(Animal & Zoo, f)]504 animal_zoos = box.recall(Animal & Zoo, f) 507 505 self.assertEqual(len(animal_zoos), 4) 508 506 names = [a.Species for a, z in animal_zoos] … … 513 511 tree = (Animal >> Zoo) >> Vet 514 512 f = logic.Expression(lambda a, z, v: z.Name == 'Sea_World') 515 azv = list(box.multirecall(tree, f)) 516 self.assertEqual(len(azv), 2) 513 self.assertEqual(len(box.recall(tree, f)), 2) 517 514 518 515 # MSAccess can't handle an INNER JOIN nested in an OUTER JOIN. … … 530 527 def set_azv(): 531 528 f = logic.Expression(lambda a, z, v: z.Name == 'Sea_World') 532 azv.append([(a, z, v) for a, z, v in 533 box.multirecall(trees[0], f)]) 529 azv.append(box.recall(trees[0], f)) 534 530 535 531 smname = arena.stores['testSM'].__class__.__name__
