def decode_offset(self, params, offset):
        """Decode an "offset token" into appropriate query parameters.

        This essentially decodes the result of encode_offset(), adjusting the
        query parameters so that we can efficiently seek to the next available
        item based on the previous query.
        """
        sort = params.get("sort", None)
        try:
            if sort == "index":
                # When sorting by sortindex, it's just a numeric offset.
                params["offset"] = int(offset)
            else:
                # When sorting by timestamp, it's a (bound, offset) pair.
                bound, offset = map(int, offset.split(":", 1))
                # Queries with "newer" should always produce bound > newer
                if bound < params.get("newer", bound):
                    raise InvalidOffsetError(offset)
                # Queries with "older" should always produce bound < older
                if bound > params.get("older", bound):
                    raise InvalidOffsetError(offset)
                # The bound determines the starting point of the sort order.
                params["offset"] = offset
                if sort == "oldest":
                    params["newer_eq"] = int(bound)
                else:
                    params["older_eq"] = int(bound)
        except ValueError:
            raise InvalidOffsetError(offset)
 def _find_items(self, session, userid, collection, **params):
     """Find items matching the given search parameters."""
     params["userid"] = userid
     params["collectionid"] = self._get_collection_id(session, collection)
     if "ttl" not in params:
         params["ttl"] = int(session.timestamp)
     if "newer" in params:
         params["newer"] = ts2bigint(params["newer"])
     # We always fetch one more item than necessary, so we can tell whether
     # there are additional items to be fetched with next_offset.
     limit = params.get("limit")
     if limit is not None:
         params["limit"] = limit + 1
     offset = params.get("offset")
     if offset is not None:
         try:
             params["offset"] = offset = int(offset)
         except ValueError:
             raise InvalidOffsetError(offset)
     rows = session.query_fetchall("FIND_ITEMS", params)
     items = [self._row_to_bso(row) for row in rows]
     # If the query returned no results, we don't know whether that's
     # because it's empty or because it doesn't exist.  Read the collection
     # timestamp and let it raise CollectionNotFoundError if necessary.
     if not items:
         self.get_collection_timestamp(session, userid, collection)
     # Check if we read past the original limit, set next_offset if so.
     next_offset = None
     if limit is not None and len(items) > limit:
         next_offset = (offset or 0) + limit
         items = items[:-1]
     return {
         "items": items,
         "next_offset": next_offset,
     }
 def get_items(self, user, **kwds):
     # Decode kwds into individual filter values.
     newer = kwds.pop("newer", None)
     older = kwds.pop("older", None)
     limit = kwds.pop("limit", None)
     offset = kwds.pop("offset", None)
     sort = kwds.pop("sort", None)
     ids = kwds.pop("ids", None)
     for unknown_kwd in kwds:
         raise TypeError("Unknown keyword argument: %s" % (unknown_kwd,))
     # Read all the items out of the cache.
     data, _ = self.get_cached_data(user)
     if data is None:
         raise CollectionNotFoundError
     # Restrict to certain item ids if specified.
     bsos_by_id = data["items"]
     if ids is not None:
         bsos = (bsos_by_id[item] for item in ids if item in bsos_by_id)
     else:
         bsos = bsos_by_id.itervalues()
     # Apply the various filters as generator expressions.
     if newer is not None:
         bsos = (bso for bso in bsos if bso["modified"] > newer)
     if older is not None:
         bsos = (bso for bso in bsos if bso["modified"] < older)
     # Filter out any that have expired.
     bsos = self._filter_expired_items(bsos)
     # Sort the resulting list.
     # We always sort so that offset/limit work correctly.
     # Using the id as a secondary key produces a unique ordering.
     bsos = list(bsos)
     if sort == "index":
         reverse = True
         key = bso_sort_key_index
     else:
         reverse = False if sort == "oldest" else True
         key = bso_sort_key_modified
     bsos.sort(key=key, reverse=reverse)
     # Trim to the specified offset, if any.
     # Note that we defaulted it to zero above.
     if offset is not None:
         try:
             offset = int(offset)
         except ValueError:
             raise InvalidOffsetError(offset)
         bsos = bsos[offset:]
     # Trim to the specified limit, if any.
     next_offset = None
     if limit is not None:
         if limit < len(bsos):
             bsos = bsos[:limit]
             next_offset = (offset or 0) + limit
     # Return the necessary information.
     return {
         "items": bsos,
         "next_offset": next_offset
     }
Exemple #4
0
 def decode_offset(self, params, offset):
     sort = params.get("sort", None)
     try:
         if sort == "index":
             # When sorting by sortindex, it's just a numeric offset.
             params["offset"] = int(offset)
         else:
             # When sorting by timestamp, it's a (bound, offset) pair.
             bound, offset = map(int, offset.split(":", 1))
             bound = bigint2ts(bound)
             # Queries with "newer" should always produce bound > newer
             if bound < params.get("newer", bound):
                 raise InvalidOffsetError(offset)
             # Queries with "older" should always produce bound < older
             if bound > params.get("older", bound):
                 raise InvalidOffsetError(offset)
             # The bound determines the starting point of the sort order.
             params["offset"] = offset
             if sort == "oldest":
                 params["newer_eq"] = bound
             else:
                 params["older_eq"] = bound
     except ValueError:
         raise InvalidOffsetError(offset)