176 for entity, key in zip(entities, keys): |
176 for entity, key in zip(entities, keys): |
177 entity._Entity__key._Key__reference.CopyFrom(key) |
177 entity._Entity__key._Key__reference.CopyFrom(key) |
178 |
178 |
179 if tx: |
179 if tx: |
180 tx.RecordModifiedKeys([e.key() for e in entities], error_on_repeat=False) |
180 tx.RecordModifiedKeys([e.key() for e in entities], error_on_repeat=False) |
|
181 tx.entity_group = entities[0].entity_group() |
181 |
182 |
182 if multiple: |
183 if multiple: |
183 return [Key._FromPb(k) for k in keys] |
184 return [Key._FromPb(k) for k in keys] |
184 else: |
185 else: |
185 return Key._FromPb(resp.key(0)) |
186 return Key._FromPb(resp.key(0)) |
356 """ |
357 """ |
357 return self.key().entity_group() |
358 return self.key().entity_group() |
358 |
359 |
359 def unindexed_properties(self): |
360 def unindexed_properties(self): |
360 """Returns this entity's unindexed properties, as a frozenset of strings.""" |
361 """Returns this entity's unindexed properties, as a frozenset of strings.""" |
361 return self.__unindexed_properties |
362 return getattr(self, '_Entity__unindexed_properties', []) |
362 |
363 |
363 def __setitem__(self, name, value): |
364 def __setitem__(self, name, value): |
364 """Implements the [] operator. Used to set property value(s). |
365 """Implements the [] operator. Used to set property value(s). |
365 |
366 |
366 If the property name is the empty string or not a string, raises |
367 If the property name is the empty string or not a string, raises |
508 sample = values |
509 sample = values |
509 if isinstance(sample, list): |
510 if isinstance(sample, list): |
510 sample = values[0] |
511 sample = values[0] |
511 |
512 |
512 if (isinstance(sample, datastore_types._RAW_PROPERTY_TYPES) or |
513 if (isinstance(sample, datastore_types._RAW_PROPERTY_TYPES) or |
513 name in self.__unindexed_properties): |
514 name in self.unindexed_properties()): |
514 pb.raw_property_list().extend(properties) |
515 pb.raw_property_list().extend(properties) |
515 else: |
516 else: |
516 pb.property_list().extend(properties) |
517 pb.property_list().extend(properties) |
517 |
518 |
518 if pb.property_size() > _MAX_INDEXED_PROPERTIES: |
519 if pb.property_size() > _MAX_INDEXED_PROPERTIES: |
858 |
859 |
859 Returns: |
860 Returns: |
860 # this query |
861 # this query |
861 Query |
862 Query |
862 """ |
863 """ |
863 key = _GetCompleteKeyOrError(ancestor) |
864 self.__ancestor = _GetCompleteKeyOrError(ancestor) |
864 self.__ancestor = datastore_pb.Reference() |
|
865 self.__ancestor.CopyFrom(key._Key__reference) |
|
866 return self |
865 return self |
867 |
866 |
868 def IsKeysOnly(self): |
867 def IsKeysOnly(self): |
869 """Returns True if this query is keys only, false otherwise.""" |
868 """Returns True if this query is keys only, false otherwise.""" |
870 return self.__keys_only |
869 return self.__keys_only |
892 limit and offset must both be integers >= 0. |
891 limit and offset must both be integers >= 0. |
893 |
892 |
894 This is not intended to be used by application developers. Use Get() |
893 This is not intended to be used by application developers. Use Get() |
895 instead! |
894 instead! |
896 """ |
895 """ |
897 if _CurrentTransactionKey(): |
|
898 raise datastore_errors.BadRequestError( |
|
899 "Can't query inside a transaction.") |
|
900 |
|
901 pb = self._ToPb(limit, offset) |
896 pb = self._ToPb(limit, offset) |
902 result = datastore_pb.QueryResult() |
897 result = datastore_pb.QueryResult() |
903 |
898 |
904 try: |
899 try: |
905 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'RunQuery', pb, result) |
900 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'RunQuery', pb, result) |
910 yaml = datastore_index.IndexYamlForQuery( |
905 yaml = datastore_index.IndexYamlForQuery( |
911 *datastore_index.CompositeIndexForQuery(pb)[1:-1]) |
906 *datastore_index.CompositeIndexForQuery(pb)[1:-1]) |
912 raise datastore_errors.NeedIndexError( |
907 raise datastore_errors.NeedIndexError( |
913 str(exc) + '\nThis query needs this index:\n' + yaml) |
908 str(exc) + '\nThis query needs this index:\n' + yaml) |
914 |
909 |
915 return Iterator._FromPb(result) |
910 return Iterator(result) |
916 |
911 |
917 def Get(self, limit, offset=0): |
912 def Get(self, limit, offset=0): |
918 """Fetches and returns a maximum number of results from the query. |
913 """Fetches and returns a maximum number of results from the query. |
919 |
914 |
920 This method fetches and returns a list of resulting entities that matched |
915 This method fetches and returns a list of resulting entities that matched |
959 if not isinstance(offset, (int, long)) or offset < 0: |
954 if not isinstance(offset, (int, long)) or offset < 0: |
960 raise datastore_errors.BadArgumentError( |
955 raise datastore_errors.BadArgumentError( |
961 'Argument to Get named \'offset\' must be an int greater than or ' |
956 'Argument to Get named \'offset\' must be an int greater than or ' |
962 'equal to 0; received %s (a %s)' % (offset, typename(offset))) |
957 'equal to 0; received %s (a %s)' % (offset, typename(offset))) |
963 |
958 |
964 return self._Run(limit, offset)._Next(limit) |
959 return self._Run(limit, offset)._Get(limit) |
965 |
960 |
966 def Count(self, limit=None): |
961 def Count(self, limit=None): |
967 """Returns the number of entities that this query matches. The returned |
962 """Returns the number of entities that this query matches. The returned |
968 count is cached; successive Count() calls will not re-scan the datastore |
963 count is cached; successive Count() calls will not re-scan the datastore |
969 unless the query is changed. |
964 unless the query is changed. |
1136 offset: int |
1131 offset: int |
1137 |
1132 |
1138 Returns: |
1133 Returns: |
1139 # the PB representation of this Query |
1134 # the PB representation of this Query |
1140 datastore_pb.Query |
1135 datastore_pb.Query |
1141 """ |
1136 |
|
1137 Raises: |
|
1138 BadRequestError if called inside a transaction and the query does not |
|
1139 include an ancestor. |
|
1140 """ |
|
1141 if not self.__ancestor and _CurrentTransactionKey(): |
|
1142 raise datastore_errors.BadRequestError( |
|
1143 'Only ancestor queries are allowed inside transactions.') |
|
1144 |
1142 pb = datastore_pb.Query() |
1145 pb = datastore_pb.Query() |
|
1146 _MaybeSetupTransaction(pb, [self.__ancestor]) |
1143 |
1147 |
1144 pb.set_kind(self.__kind.encode('utf-8')) |
1148 pb.set_kind(self.__kind.encode('utf-8')) |
1145 pb.set_keys_only(bool(self.__keys_only)) |
1149 pb.set_keys_only(bool(self.__keys_only)) |
1146 if self.__app: |
1150 if self.__app: |
1147 pb.set_app(self.__app.encode('utf-8')) |
1151 pb.set_app(self.__app.encode('utf-8')) |
1148 if limit is not None: |
1152 if limit is not None: |
1149 pb.set_limit(limit) |
1153 pb.set_limit(limit) |
1150 if offset is not None: |
1154 if offset is not None: |
1151 pb.set_offset(offset) |
1155 pb.set_offset(offset) |
1152 if self.__ancestor: |
1156 if self.__ancestor: |
1153 pb.mutable_ancestor().CopyFrom(self.__ancestor) |
1157 pb.mutable_ancestor().CopyFrom(self.__ancestor._Key__reference) |
1154 |
1158 |
1155 if ((self.__hint == self.ORDER_FIRST and self.__orderings) or |
1159 if ((self.__hint == self.ORDER_FIRST and self.__orderings) or |
1156 (self.__hint == self.ANCESTOR_FIRST and self.__ancestor) or |
1160 (self.__hint == self.ANCESTOR_FIRST and self.__ancestor) or |
1157 (self.__hint == self.FILTER_FIRST and len(self) > 0)): |
1161 (self.__hint == self.FILTER_FIRST and len(self) > 0)): |
1158 pb.set_hint(self.__hint) |
1162 pb.set_hint(self.__hint) |
1511 |
1515 |
1512 > it = Query('Person').Run() |
1516 > it = Query('Person').Run() |
1513 > for person in it: |
1517 > for person in it: |
1514 > print 'Hi, %s!' % person['name'] |
1518 > print 'Hi, %s!' % person['name'] |
1515 """ |
1519 """ |
1516 def __init__(self, cursor, keys_only=False): |
1520 def __init__(self, query_result_pb): |
1517 self.__cursor = cursor |
1521 self.__cursor = query_result_pb.cursor() |
1518 self.__buffer = [] |
1522 self.__keys_only = query_result_pb.keys_only() |
1519 self.__more_results = True |
1523 self.__buffer = self._ProcessQueryResult(query_result_pb) |
1520 self.__keys_only = keys_only |
1524 |
|
1525 def _Get(self, count): |
|
1526 """Gets the next count result(s) of the query. |
|
1527 |
|
1528 Not intended to be used by application developers. Use the python |
|
1529 iterator protocol instead. |
|
1530 |
|
1531 This method uses _Next to returns the next entities or keys from the list of |
|
1532 matching results. If the query specified a sort order, results are returned |
|
1533 in that order. Otherwise, the order is undefined. |
|
1534 |
|
1535 The argument, count, specifies the number of results to return. However, the |
|
1536 length of the returned list may be smaller than count. This is the case only |
|
1537 if count is greater than the number of remaining results. |
|
1538 |
|
1539 The results are always returned as a list. If there are no results left, |
|
1540 an empty list is returned. |
|
1541 |
|
1542 Args: |
|
1543 # the number of results to return; must be >= 1 |
|
1544 count: int or long |
|
1545 |
|
1546 Returns: |
|
1547 # a list of entities or keys |
|
1548 [Entity or Key, ...] |
|
1549 """ |
|
1550 entityList = self._Next(count) |
|
1551 while len(entityList) < count and self.__more_results: |
|
1552 next_results = self._Next(count - len(entityList)) |
|
1553 if not next_results: |
|
1554 break |
|
1555 entityList += next_results |
|
1556 return entityList; |
1521 |
1557 |
1522 def _Next(self, count): |
1558 def _Next(self, count): |
1523 """Returns the next result(s) of the query. |
1559 """Returns the next result(s) of the query. |
1524 |
1560 |
1525 Not intended to be used by application developers. Use the python |
1561 Not intended to be used by application developers. Use the python |
1527 |
1563 |
1528 This method returns the next entities or keys from the list of matching |
1564 This method returns the next entities or keys from the list of matching |
1529 results. If the query specified a sort order, results are returned in that |
1565 results. If the query specified a sort order, results are returned in that |
1530 order. Otherwise, the order is undefined. |
1566 order. Otherwise, the order is undefined. |
1531 |
1567 |
1532 The argument specifies the number of results to return. If it's greater |
1568 The argument, count, specifies the number of results to return. However, the |
1533 than the number of remaining results, all of the remaining results are |
1569 length of the returned list may be smaller than count. This is the case if |
1534 returned. In that case, the length of the returned list will be smaller |
1570 count is greater than the number of remaining results or the size of the |
1535 than count. |
1571 remaining results exciteds the RPC buffer limit. Use _Get to insure all |
|
1572 possible entities are retrieved. |
1536 |
1573 |
1537 There is an internal buffer for use with the next() method. If this buffer |
1574 There is an internal buffer for use with the next() method. If this buffer |
1538 is not empty, up to 'count' values are removed from this buffer and |
1575 is not empty, up to 'count' values are removed from this buffer and |
1539 returned. It's best not to mix _Next() and next(). |
1576 returned. It's best not to mix _Next() and next(). |
1540 |
1577 |
1553 raise datastore_errors.BadArgumentError( |
1590 raise datastore_errors.BadArgumentError( |
1554 'Argument to _Next must be an int greater than 0; received %s (a %s)' % |
1591 'Argument to _Next must be an int greater than 0; received %s (a %s)' % |
1555 (count, typename(count))) |
1592 (count, typename(count))) |
1556 |
1593 |
1557 if self.__buffer: |
1594 if self.__buffer: |
1558 raise datastore_errors.BadRequestError( |
1595 if count <= len(self.__buffer): |
1559 'You can\'t mix next() and _Next()') |
1596 entity_list = self.__buffer[:count] |
|
1597 del self.__buffer[:count] |
|
1598 return entity_list |
|
1599 else: |
|
1600 entity_list = self.__buffer |
|
1601 self.__buffer = [] |
|
1602 count -= len(entity_list) |
|
1603 else: |
|
1604 entity_list=[] |
1560 |
1605 |
1561 if not self.__more_results: |
1606 if not self.__more_results: |
1562 return [] |
1607 return entity_list |
1563 |
1608 |
1564 req = datastore_pb.NextRequest() |
1609 req = datastore_pb.NextRequest() |
1565 req.set_count(count) |
1610 req.set_count(count) |
1566 req.mutable_cursor().CopyFrom(self._ToPb()) |
1611 req.mutable_cursor().CopyFrom(self.__cursor) |
1567 result = datastore_pb.QueryResult() |
1612 result = datastore_pb.QueryResult() |
1568 try: |
1613 try: |
1569 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Next', req, result) |
1614 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Next', req, result) |
1570 except apiproxy_errors.ApplicationError, err: |
1615 except apiproxy_errors.ApplicationError, err: |
1571 raise _ToDatastoreError(err) |
1616 raise _ToDatastoreError(err) |
1572 |
1617 |
|
1618 return entity_list + self._ProcessQueryResult(result) |
|
1619 |
|
1620 def _ProcessQueryResult(self, result): |
|
1621 """Returns all results from datastore_pb.QueryResult and updates |
|
1622 self.__more_results |
|
1623 |
|
1624 Not intended to be used by application developers. Use the python |
|
1625 iterator protocol instead. |
|
1626 |
|
1627 The results are always returned as a list. If there are no results left, |
|
1628 an empty list is returned. |
|
1629 |
|
1630 Args: |
|
1631 # the instance of datastore_pb.QueryResult to be stored |
|
1632 result: datastore_pb.QueryResult |
|
1633 |
|
1634 Returns: |
|
1635 # a list of entities or keys |
|
1636 [Entity or Key, ...] |
|
1637 """ |
1573 self.__more_results = result.more_results() |
1638 self.__more_results = result.more_results() |
1574 |
1639 |
1575 if self.__keys_only: |
1640 if self.__keys_only: |
1576 return [Key._FromPb(e.key()) for e in result.result_list()] |
1641 return [Key._FromPb(e.key()) for e in result.result_list()] |
1577 else: |
1642 else: |
1586 return self.__buffer.pop(0) |
1651 return self.__buffer.pop(0) |
1587 except IndexError: |
1652 except IndexError: |
1588 raise StopIteration |
1653 raise StopIteration |
1589 |
1654 |
1590 def __iter__(self): return self |
1655 def __iter__(self): return self |
1591 |
|
1592 def _ToPb(self): |
|
1593 """Converts this Iterator to its protocol buffer representation. Not |
|
1594 intended to be used by application developers. Enforced by hiding the |
|
1595 datastore_pb classes. |
|
1596 |
|
1597 Returns: |
|
1598 # the PB representation of this Iterator |
|
1599 datastore_pb.Cursor |
|
1600 """ |
|
1601 pb = datastore_pb.Cursor() |
|
1602 pb.set_cursor(self.__cursor) |
|
1603 return pb |
|
1604 |
|
1605 @staticmethod |
|
1606 def _FromPb(pb): |
|
1607 """Static factory method. Returns the Iterator representation of the given |
|
1608 protocol buffer (datastore_pb.QueryResult). Not intended to be used by |
|
1609 application developers. Enforced by hiding the datastore_pb classes. |
|
1610 |
|
1611 Args: |
|
1612 pb: datastore_pb.QueryResult |
|
1613 |
|
1614 Returns: |
|
1615 Iterator |
|
1616 """ |
|
1617 return Iterator(pb.cursor().cursor(), keys_only=pb.keys_only()) |
|
1618 |
|
1619 |
1656 |
1620 class _Transaction(object): |
1657 class _Transaction(object): |
1621 """Encapsulates a transaction currently in progress. |
1658 """Encapsulates a transaction currently in progress. |
1622 |
1659 |
1623 If we've sent a BeginTransaction call, then handle will be a |
1660 If we've sent a BeginTransaction call, then handle will be a |
1638 self.modified_keys = set() |
1675 self.modified_keys = set() |
1639 |
1676 |
1640 def RecordModifiedKeys(self, keys, error_on_repeat=True): |
1677 def RecordModifiedKeys(self, keys, error_on_repeat=True): |
1641 """Updates the modified keys seen so far. |
1678 """Updates the modified keys seen so far. |
1642 |
1679 |
1643 Also sets entity_group if it hasn't yet been set. |
|
1644 |
|
1645 If error_on_repeat is True and any of the given keys have already been |
1680 If error_on_repeat is True and any of the given keys have already been |
1646 modified, raises BadRequestError. |
1681 modified, raises BadRequestError. |
1647 |
1682 |
1648 Args: |
1683 Args: |
1649 keys: sequence of Keys |
1684 keys: sequence of Keys |
1650 """ |
1685 """ |
1651 keys, _ = NormalizeAndTypeCheckKeys(keys) |
1686 keys, _ = NormalizeAndTypeCheckKeys(keys) |
1652 |
|
1653 if keys and not self.entity_group: |
|
1654 self.entity_group = keys[0].entity_group() |
|
1655 |
|
1656 keys = set(keys) |
1687 keys = set(keys) |
1657 |
1688 |
1658 if error_on_repeat: |
1689 if error_on_repeat: |
1659 already_modified = self.modified_keys.intersection(keys) |
1690 already_modified = self.modified_keys.intersection(keys) |
1660 if already_modified: |
1691 if already_modified: |
1832 |
1863 |
1833 Raises BadRequestError if the entity has a different entity group than the |
1864 Raises BadRequestError if the entity has a different entity group than the |
1834 current transaction. |
1865 current transaction. |
1835 |
1866 |
1836 Args: |
1867 Args: |
1837 request: GetRequest, PutRequest, or DeleteRequest |
1868 request: GetRequest, PutRequest, DeleteRequest, or Query |
1838 keys: sequence of Keys |
1869 keys: sequence of Keys |
1839 |
1870 |
1840 Returns: |
1871 Returns: |
1841 _Transaction if we're inside a transaction, otherwise None |
1872 _Transaction if we're inside a transaction, otherwise None |
1842 """ |
1873 """ |
1843 assert isinstance(request, (datastore_pb.GetRequest, datastore_pb.PutRequest, |
1874 assert isinstance(request, (datastore_pb.GetRequest, datastore_pb.PutRequest, |
1844 datastore_pb.DeleteRequest)) |
1875 datastore_pb.DeleteRequest, datastore_pb.Query)) |
1845 tx_key = None |
1876 tx_key = None |
1846 |
1877 |
1847 try: |
1878 try: |
1848 tx_key = _CurrentTransactionKey() |
1879 tx_key = _CurrentTransactionKey() |
1849 if tx_key: |
1880 if tx_key: |
1863 |
1895 |
1864 |
1896 |
1865 |
1897 |
1866 (not group.has_id_or_name() and group is not expected_group)): |
1898 (not group.has_id_or_name() and group is not expected_group)): |
1867 raise _DifferentEntityGroupError(expected_group, group) |
1899 raise _DifferentEntityGroupError(expected_group, group) |
|
1900 |
|
1901 if not tx.entity_group and group.has_id_or_name(): |
|
1902 tx.entity_group = group |
1868 |
1903 |
1869 if not tx.handle: |
1904 if not tx.handle: |
1870 tx.handle = datastore_pb.Transaction() |
1905 tx.handle = datastore_pb.Transaction() |
1871 req = api_base_pb.VoidProto() |
1906 req = api_base_pb.VoidProto() |
1872 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'BeginTransaction', req, |
1907 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'BeginTransaction', req, |