def _compare(self, other): """Compare this result to another result for sorting. Args: other (_Result): The other result to compare to. Returns: int: :data:`-1` if this result should come before `other`, :data:`0` if this result is equivalent to `other` for sorting purposes, or :data:`1` if this result should come after `other`. Raises: NotImplemented: If `order_by` was not passed to constructor or is :data:`None` or is empty. NotImplemented: If `other` is not a `_Result`. """ if not self.order_by: raise NotImplementedError("Can't sort result set without order_by") if not isinstance(other, _Result): return NotImplemented for order in self.order_by: if order.name == "__key__": this_value = helpers.key_from_protobuf( self.result_pb.entity.key).flat_path other_value = helpers.key_from_protobuf( other.result_pb.entity.key).flat_path else: this_value_pb = self.result_pb.entity.properties[order.name] this_value = helpers._get_value_from_value_pb(this_value_pb) other_value_pb = other.result_pb.entity.properties[order.name] other_value = helpers._get_value_from_value_pb(other_value_pb) # Compare key paths if ordering by key property if isinstance(this_value, Key): this_value = this_value.flat_path if isinstance(other_value, Key): other_value = other_value.flat_path direction = -1 if order.reverse else 1 if this_value < other_value: return -direction elif this_value > other_value: return direction return 0
def _matches_property_filter( self, stored_obj: _StoredObject, prop_filter: types.PropertyFilter ) -> bool: op = prop_filter.op name = prop_filter.property.name filter_val = ds_helpers._get_value_from_value_pb(prop_filter.value) # Handle ancestor queries. Only supports a single level for now if op == types.PropertyFilter.Operator.HAS_ANCESTOR: assert ( name == "__key__" and prop_filter.value.WhichOneof("value_type") == "key_value" ) assert len(prop_filter.value.key_value.path) == 1 return ( prop_filter.value.key_value.path[0] in stored_obj.entity.key.path[:-1] ) # If the field we're looking for doesn't exist on this model, bail if name not in stored_obj.entity.properties: return False # Otherwise, compare the field against the value in the filter prop_val_pb = stored_obj.entity.properties[name] prop_val = ds_helpers._get_value_from_value_pb(prop_val_pb) method_name = self._OPERATOR_TO_CMP_METHOD_NAME.get(op) assert method_name prop_type = prop_val_pb.WhichOneof("value_type") if prop_type == "array_value": # For repeated properties, we need to unpack them res = any(getattr(p, method_name)(filter_val) is True for p in prop_val) elif filter_val is None: # Need to mimic python2 comparisons, where None is smaller than anything if op == types.PropertyFilter.Operator.GREATER_THAN_OR_EQUAL: res = True elif op == types.PropertyFilter.Operator.LESS_THAN_OR_EQUAL: res = prop_val is None elif op == types.PropertyFilter.Operator.LESS_THAN: res = False elif op == types.PropertyFilter.Operator.GREATER_THAN: res = prop_val is not None elif op == types.PropertyFilter.Operator.EQUAL: res = prop_val is None else: res = False else: res = getattr(prop_val, method_name)(filter_val) if res is NotImplemented: return False return res
def _call_fut(self, pb): from google.cloud.datastore.helpers import _get_value_from_value_pb return _get_value_from_value_pb(pb)
def _call_fut(self, pb): from google.cloud.datastore.helpers import _get_value_from_value_pb return _get_value_from_value_pb(pb)
def _run_query( self, request: types.RunQueryRequest, *args, **kwargs ) -> types.RunQueryResponse: # Don't support cloud sql # TODO also figire out error handling assert request.query # Query processing will be very naive. query: types.Query = request.query transaction_id: bytes = request.read_options.transaction resp_data: List[_StoredObject] = [] for _, stored in self.store.items(transaction_id): if query.kind and stored.entity.key.path[-1].kind != query.kind[0].name: continue if self._matches_filter(stored, query.filter): resp_data.append(stored) if query.order: # TODO assert len(query.order) == 1 order = query.order[0] assert order.direction in [ types.PropertyOrder.Direction.DESCENDING, types.PropertyOrder.Direction.ASCENDING, ] resp_data.sort( key=lambda d: ds_helpers._get_value_from_value_pb( d.entity.properties.get(order.property.name) ), reverse=order.direction == types.PropertyOrder.Direction.DESCENDING, ) original_length = len(resp_data) skipped_results = 0 results_after_limit = 0 # Handle offset resp_data = resp_data[query.offset :] skipped_results = original_length - len(resp_data) if query.HasField("limit"): resp_data = resp_data[: query.limit.value] if query.projection: projection_fields = [p.property.name for p in query.projection] if projection_fields == ["__key__"]: result_type = types.EntityResult.ResultType.KEY_ONLY entity_results = [ types.EntityResult( entity=types.Entity(key=resp.entity.key), version=resp.version ) for resp in resp_data ] else: result_type = types.EntityResult.ResultType.PROJECTION entity_results = [ types.EntityResult( entity=types.Entity( key=resp.entity.key, properties={ k: v for k, v in resp.entity.properties.items() if k in projection_fields }, ), version=resp.version, ) for resp in resp_data ] else: result_type = types.EntityResult.ResultType.FULL entity_results = [ types.EntityResult(entity=resp.entity, version=resp.version) for resp in resp_data ] if results_after_limit > 0: more_results = ( types.QueryResultBatch.MoreResultsType.MORE_RESULTS_AFTER_LIMIT ) else: more_results = types.QueryResultBatch.MoreResultsType.NO_MORE_RESULTS return types.RunQueryResponse( batch=types.QueryResultBatch( skipped_results=skipped_results, more_results=more_results, entity_result_type=result_type, entity_results=entity_results, snapshot_version=self.store.seqid(transaction_id), ) )