@@ -428,7 +428,14 @@ def test_sql_injection_in_path_rejected(self):
428428class TestSort :
429429 def test_sort_ascending (self ):
430430 qr = build_query ({"portal_type" : "Document" , "sort_on" : "sortable_title" })
431- assert qr ["order_by" ] == "idx->>'sortable_title' ASC"
431+ assert qr ["order_by" ] == "idx->'sortable_title' ASC"
432+
433+ def test_sort_field_uses_jsonb_operator (self ):
434+ """FIELD sort must use `->` (jsonb) not `->>` (text) so numeric
435+ FieldIndexes sort numerically instead of lexicographically (#158)."""
436+ qr = build_query ({"sort_on" : "sortable_title" })
437+ assert "idx->'sortable_title'" in qr ["order_by" ]
438+ assert "idx->>'sortable_title'" not in qr ["order_by" ]
432439
433440 def test_sort_descending (self ):
434441 qr = build_query (
@@ -481,7 +488,7 @@ def test_sort_on_list_two_fields(self):
481488 "sort_on" : ["portal_type" , "sortable_title" ],
482489 }
483490 )
484- assert qr ["order_by" ] == "idx->> 'portal_type' ASC, idx-> >'sortable_title' ASC"
491+ assert qr ["order_by" ] == "idx->'portal_type' ASC, idx->'sortable_title' ASC"
485492
486493 def test_sort_on_list_with_mixed_orders (self ):
487494 qr = build_query (
@@ -494,7 +501,7 @@ def test_sort_on_list_with_mixed_orders(self):
494501 assert qr ["order_by" ] is not None
495502 parts = qr ["order_by" ].split (", " )
496503 assert len (parts ) == 2
497- assert parts [0 ] == "idx->> 'sortable_title' ASC"
504+ assert parts [0 ] == "idx->'sortable_title' ASC"
498505 assert "pgcatalog_to_timestamptz" in parts [1 ]
499506 assert "DESC" in parts [1 ]
500507
@@ -506,7 +513,7 @@ def test_sort_on_list_single_order_applies_to_all(self):
506513 "sort_order" : "descending" ,
507514 }
508515 )
509- assert qr ["order_by" ] == "idx->> 'portal_type' DESC, idx-> >'sortable_title' DESC"
516+ assert qr ["order_by" ] == "idx->'portal_type' DESC, idx->'sortable_title' DESC"
510517
511518 def test_sort_on_list_with_unknown_index_skipped (self ):
512519 qr = build_query (
@@ -515,7 +522,7 @@ def test_sort_on_list_with_unknown_index_skipped(self):
515522 "sort_on" : ["nonexistent" , "sortable_title" ],
516523 }
517524 )
518- assert qr ["order_by" ] == "idx->> 'sortable_title' ASC"
525+ assert qr ["order_by" ] == "idx->'sortable_title' ASC"
519526
520527 def test_sort_on_list_all_unknown (self ):
521528 qr = build_query (
@@ -529,7 +536,7 @@ def test_sort_on_list_all_unknown(self):
529536 def test_sort_on_single_string_still_works (self ):
530537 """Backward compat: single string sort_on unchanged."""
531538 qr = build_query ({"portal_type" : "Document" , "sort_on" : "sortable_title" })
532- assert qr ["order_by" ] == "idx->> 'sortable_title' ASC"
539+ assert qr ["order_by" ] == "idx->'sortable_title' ASC"
533540
534541
535542# ---------------------------------------------------------------------------
@@ -742,13 +749,13 @@ def test_tgpath_multiple_paths_subtree(self):
742749
743750 def test_tgpath_sort (self ):
744751 qr = build_query ({"tgpath" : "/uuid1" , "sort_on" : "tgpath" })
745- assert qr ["order_by" ] == "idx->> 'tgpath' ASC"
752+ assert qr ["order_by" ] == "idx->'tgpath' ASC"
746753
747754 def test_tgpath_sort_descending (self ):
748755 qr = build_query (
749756 {"tgpath" : "/uuid1" , "sort_on" : "tgpath" , "sort_order" : "descending" }
750757 )
751- assert qr ["order_by" ] == "idx->> 'tgpath' DESC"
758+ assert qr ["order_by" ] == "idx->'tgpath' DESC"
752759
753760 def test_builtin_path_uses_typed_columns (self ):
754761 """Built-in 'path' index dispatches to typed columns, not idx JSONB (#132)."""
@@ -1054,7 +1061,7 @@ def test_sort_dynamic_field(self, populated_registry):
10541061 get_registry ().register ("my_sort_field" , IndexType .FIELD , "my_sort_field" )
10551062
10561063 qr = build_query ({"sort_on" : "my_sort_field" })
1057- assert qr ["order_by" ] == "idx->> 'my_sort_field' ASC"
1064+ assert qr ["order_by" ] == "idx->'my_sort_field' ASC"
10581065
10591066 def test_sort_dynamic_date (self , populated_registry ):
10601067 from plone .pgcatalog .columns import get_registry
0 commit comments