def _comparator(self, doc1, doc2) -> Any: _orders = self._orders # Add implicit sorting by name, using the last specified direction. if len(_orders) == 0: lastDirection = BaseQuery.ASCENDING else: if _orders[-1].direction == 1: lastDirection = BaseQuery.ASCENDING else: lastDirection = BaseQuery.DESCENDING orderBys = list(_orders) order_pb = query.StructuredQuery.Order( field=query.StructuredQuery.FieldReference(field_path="id"), direction=_enum_from_direction(lastDirection), ) orderBys.append(order_pb) for orderBy in orderBys: if orderBy.field.field_path == "id": # If ordering by docuent id, compare resource paths. comp = Order()._compare_to(doc1.reference._path, doc2.reference._path) else: if ( orderBy.field.field_path not in doc1._data or orderBy.field.field_path not in doc2._data ): raise ValueError( "Can only compare fields that exist in the " "DocumentSnapshot. Please include the fields you are " "ordering on in your select() call." ) v1 = doc1._data[orderBy.field.field_path] v2 = doc2._data[orderBy.field.field_path] encoded_v1 = _helpers.encode_value(v1) encoded_v2 = _helpers.encode_value(v2) comp = Order().compare(encoded_v1, encoded_v2) if comp != 0: # 1 == Ascending, -1 == Descending return orderBy.direction * comp return 0
def _make_order(*args, **kwargs): from google.cloud.firestore_v1.order import Order return Order(*args, **kwargs)
def test_order(self): # Constants used to represent min/max values of storage types. int_max_value = 2 ** 31 - 1 int_min_value = -(2 ** 31) float_min_value = 1.175494351 ** -38 float_nan = float("nan") inf = float("inf") groups = [None] * 65 groups[0] = [nullValue()] groups[1] = [_boolean_value(False)] groups[2] = [_boolean_value(True)] # numbers groups[3] = [_double_value(float_nan), _double_value(float_nan)] groups[4] = [_double_value(-inf)] groups[5] = [_int_value(int_min_value - 1)] groups[6] = [_int_value(int_min_value)] groups[7] = [_double_value(-1.1)] # Integers and Doubles order the same. groups[8] = [_int_value(-1), _double_value(-1.0)] groups[9] = [_double_value(-float_min_value)] # zeros all compare the same. groups[10] = [ _int_value(0), _double_value(-0.0), _double_value(0.0), _double_value(+0.0), ] groups[11] = [_double_value(float_min_value)] groups[12] = [_int_value(1), _double_value(1.0)] groups[13] = [_double_value(1.1)] groups[14] = [_int_value(int_max_value)] groups[15] = [_int_value(int_max_value + 1)] groups[16] = [_double_value(inf)] groups[17] = [_timestamp_value(123, 0)] groups[18] = [_timestamp_value(123, 123)] groups[19] = [_timestamp_value(345, 0)] # strings groups[20] = [_string_value("")] groups[21] = [_string_value("\u0000\ud7ff\ue000\uffff")] groups[22] = [_string_value("(╯°□°)╯︵ ┻━┻")] groups[23] = [_string_value("a")] groups[24] = [_string_value("abc def")] # latin small letter e + combining acute accent + latin small letter b groups[25] = [_string_value("e\u0301b")] groups[26] = [_string_value("æ")] # latin small letter e with acute accent + latin small letter a groups[27] = [_string_value("\u00e9a")] # blobs groups[28] = [_blob_value(b"")] groups[29] = [_blob_value(b"\x00")] groups[30] = [_blob_value(b"\x00\x01\x02\x03\x04")] groups[31] = [_blob_value(b"\x00\x01\x02\x04\x03")] groups[32] = [_blob_value(b"\x7f")] # resource names groups[33] = [_reference_value("projects/p1/databases/d1/documents/c1/doc1")] groups[34] = [_reference_value("projects/p1/databases/d1/documents/c1/doc2")] groups[35] = [ _reference_value("projects/p1/databases/d1/documents/c1/doc2/c2/doc1") ] groups[36] = [ _reference_value("projects/p1/databases/d1/documents/c1/doc2/c2/doc2") ] groups[37] = [_reference_value("projects/p1/databases/d1/documents/c10/doc1")] groups[38] = [_reference_value("projects/p1/databases/d1/documents/c2/doc1")] groups[39] = [_reference_value("projects/p2/databases/d2/documents/c1/doc1")] groups[40] = [_reference_value("projects/p2/databases/d2/documents/c1-/doc1")] groups[41] = [_reference_value("projects/p2/databases/d3/documents/c1-/doc1")] # geo points groups[42] = [_geoPoint_value(-90, -180)] groups[43] = [_geoPoint_value(-90, 0)] groups[44] = [_geoPoint_value(-90, 180)] groups[45] = [_geoPoint_value(0, -180)] groups[46] = [_geoPoint_value(0, 0)] groups[47] = [_geoPoint_value(0, 180)] groups[48] = [_geoPoint_value(1, -180)] groups[49] = [_geoPoint_value(1, 0)] groups[50] = [_geoPoint_value(1, 180)] groups[51] = [_geoPoint_value(90, -180)] groups[52] = [_geoPoint_value(90, 0)] groups[53] = [_geoPoint_value(90, 180)] # arrays groups[54] = [_array_value()] groups[55] = [_array_value(["bar"])] groups[56] = [_array_value(["foo"])] groups[57] = [_array_value(["foo", 0])] groups[58] = [_array_value(["foo", 1])] groups[59] = [_array_value(["foo", "0"])] # objects groups[60] = [_object_value({"bar": 0})] groups[61] = [_object_value({"bar": 0, "foo": 1})] groups[62] = [_object_value({"bar": 1})] groups[63] = [_object_value({"bar": 2})] groups[64] = [_object_value({"bar": "0"})] target = self._make_one() for i in range(len(groups)): for left in groups[i]: for j in range(len(groups)): for right in groups[j]: expected = Order._compare_to(i, j) self.assertEqual( target.compare(left, right), expected, "comparing L->R {} ({}) to {} ({})".format( i, left, j, right ), ) expected = Order._compare_to(j, i) self.assertEqual( target.compare(right, left), expected, "comparing R->L {} ({}) to {} ({})".format( j, right, i, left ), )