1use std::{collections::HashMap, sync::LazyLock};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11#[non_exhaustive]
12pub enum OperatorCategory {
13 Comparison,
15 String,
17 Null,
19 Array,
21 Vector,
23 Fulltext,
25 Containment,
27 Network,
29 DateRange,
31 Ltree,
33 Spatial,
35 Path,
37}
38
39#[derive(Debug, Clone)]
41pub struct OperatorInfo {
42 pub name: &'static str,
44 pub sql_op: &'static str,
46 pub category: OperatorCategory,
48 pub requires_array: bool,
50 pub jsonb_operator: bool,
52}
53
54pub static OPERATOR_REGISTRY: LazyLock<HashMap<&'static str, OperatorInfo>> = LazyLock::new(|| {
56 let mut m = HashMap::new();
57
58 m.insert(
60 "eq",
61 OperatorInfo {
62 name: "eq",
63 sql_op: "=",
64 category: OperatorCategory::Comparison,
65 requires_array: false,
66 jsonb_operator: false,
67 },
68 );
69
70 m.insert(
71 "ne",
72 OperatorInfo {
73 name: "ne",
74 sql_op: "!=",
75 category: OperatorCategory::Comparison,
76 requires_array: false,
77 jsonb_operator: false,
78 },
79 );
80
81 m.insert(
82 "gt",
83 OperatorInfo {
84 name: "gt",
85 sql_op: ">",
86 category: OperatorCategory::Comparison,
87 requires_array: false,
88 jsonb_operator: false,
89 },
90 );
91
92 m.insert(
93 "gte",
94 OperatorInfo {
95 name: "gte",
96 sql_op: ">=",
97 category: OperatorCategory::Comparison,
98 requires_array: false,
99 jsonb_operator: false,
100 },
101 );
102
103 m.insert(
104 "lt",
105 OperatorInfo {
106 name: "lt",
107 sql_op: "<",
108 category: OperatorCategory::Comparison,
109 requires_array: false,
110 jsonb_operator: false,
111 },
112 );
113
114 m.insert(
115 "lte",
116 OperatorInfo {
117 name: "lte",
118 sql_op: "<=",
119 category: OperatorCategory::Comparison,
120 requires_array: false,
121 jsonb_operator: false,
122 },
123 );
124
125 m.insert(
126 "in",
127 OperatorInfo {
128 name: "in",
129 sql_op: "IN",
130 category: OperatorCategory::Comparison,
131 requires_array: true,
132 jsonb_operator: false,
133 },
134 );
135
136 m.insert(
137 "nin",
138 OperatorInfo {
139 name: "nin",
140 sql_op: "NOT IN",
141 category: OperatorCategory::Comparison,
142 requires_array: true,
143 jsonb_operator: false,
144 },
145 );
146
147 m.insert(
149 "like",
150 OperatorInfo {
151 name: "like",
152 sql_op: "LIKE",
153 category: OperatorCategory::String,
154 requires_array: false,
155 jsonb_operator: false,
156 },
157 );
158
159 m.insert(
160 "ilike",
161 OperatorInfo {
162 name: "ilike",
163 sql_op: "ILIKE",
164 category: OperatorCategory::String,
165 requires_array: false,
166 jsonb_operator: false,
167 },
168 );
169
170 m.insert(
171 "nlike",
172 OperatorInfo {
173 name: "nlike",
174 sql_op: "NOT LIKE",
175 category: OperatorCategory::String,
176 requires_array: false,
177 jsonb_operator: false,
178 },
179 );
180
181 m.insert(
182 "nilike",
183 OperatorInfo {
184 name: "nilike",
185 sql_op: "NOT ILIKE",
186 category: OperatorCategory::String,
187 requires_array: false,
188 jsonb_operator: false,
189 },
190 );
191
192 m.insert(
193 "regex",
194 OperatorInfo {
195 name: "regex",
196 sql_op: "~",
197 category: OperatorCategory::String,
198 requires_array: false,
199 jsonb_operator: false,
200 },
201 );
202
203 m.insert(
204 "iregex",
205 OperatorInfo {
206 name: "iregex",
207 sql_op: "~*",
208 category: OperatorCategory::String,
209 requires_array: false,
210 jsonb_operator: false,
211 },
212 );
213
214 m.insert(
215 "nregex",
216 OperatorInfo {
217 name: "nregex",
218 sql_op: "!~",
219 category: OperatorCategory::String,
220 requires_array: false,
221 jsonb_operator: false,
222 },
223 );
224
225 m.insert(
226 "niregex",
227 OperatorInfo {
228 name: "niregex",
229 sql_op: "!~*",
230 category: OperatorCategory::String,
231 requires_array: false,
232 jsonb_operator: false,
233 },
234 );
235
236 m.insert(
238 "is_null",
239 OperatorInfo {
240 name: "is_null",
241 sql_op: "IS NULL",
242 category: OperatorCategory::Null,
243 requires_array: false,
244 jsonb_operator: false,
245 },
246 );
247
248 m.insert(
249 "is_not_null",
250 OperatorInfo {
251 name: "is_not_null",
252 sql_op: "IS NOT NULL",
253 category: OperatorCategory::Null,
254 requires_array: false,
255 jsonb_operator: false,
256 },
257 );
258
259 m.insert(
261 "contains",
262 OperatorInfo {
263 name: "contains",
264 sql_op: "@>",
265 category: OperatorCategory::Containment,
266 requires_array: false,
267 jsonb_operator: true,
268 },
269 );
270
271 m.insert(
272 "contained_in",
273 OperatorInfo {
274 name: "contained_in",
275 sql_op: "<@",
276 category: OperatorCategory::Containment,
277 requires_array: false,
278 jsonb_operator: true,
279 },
280 );
281
282 m.insert(
283 "has_key",
284 OperatorInfo {
285 name: "has_key",
286 sql_op: "?",
287 category: OperatorCategory::Containment,
288 requires_array: false,
289 jsonb_operator: true,
290 },
291 );
292
293 m.insert(
294 "has_any_keys",
295 OperatorInfo {
296 name: "has_any_keys",
297 sql_op: "?|",
298 category: OperatorCategory::Containment,
299 requires_array: true,
300 jsonb_operator: true,
301 },
302 );
303
304 m.insert(
305 "has_all_keys",
306 OperatorInfo {
307 name: "has_all_keys",
308 sql_op: "?&",
309 category: OperatorCategory::Containment,
310 requires_array: true,
311 jsonb_operator: true,
312 },
313 );
314
315 m.insert(
317 "array_contains",
318 OperatorInfo {
319 name: "array_contains",
320 sql_op: "@>",
321 category: OperatorCategory::Array,
322 requires_array: false,
323 jsonb_operator: false,
324 },
325 );
326
327 m.insert(
328 "array_contained_in",
329 OperatorInfo {
330 name: "array_contained_in",
331 sql_op: "<@",
332 category: OperatorCategory::Array,
333 requires_array: false,
334 jsonb_operator: false,
335 },
336 );
337
338 m.insert(
339 "array_overlaps",
340 OperatorInfo {
341 name: "array_overlaps",
342 sql_op: "&&",
343 category: OperatorCategory::Array,
344 requires_array: false,
345 jsonb_operator: false,
346 },
347 );
348
349 m.insert(
351 "cosine_distance",
352 OperatorInfo {
353 name: "cosine_distance",
354 sql_op: "<=>",
355 category: OperatorCategory::Vector,
356 requires_array: false,
357 jsonb_operator: false,
358 },
359 );
360
361 m.insert(
362 "l2_distance",
363 OperatorInfo {
364 name: "l2_distance",
365 sql_op: "<->",
366 category: OperatorCategory::Vector,
367 requires_array: false,
368 jsonb_operator: false,
369 },
370 );
371
372 m.insert(
373 "inner_product",
374 OperatorInfo {
375 name: "inner_product",
376 sql_op: "<#>",
377 category: OperatorCategory::Vector,
378 requires_array: false,
379 jsonb_operator: false,
380 },
381 );
382
383 m.insert(
384 "l1_distance",
385 OperatorInfo {
386 name: "l1_distance",
387 sql_op: "<+>",
388 category: OperatorCategory::Vector,
389 requires_array: false,
390 jsonb_operator: false,
391 },
392 );
393
394 m.insert(
395 "hamming_distance",
396 OperatorInfo {
397 name: "hamming_distance",
398 sql_op: "<~>",
399 category: OperatorCategory::Vector,
400 requires_array: false,
401 jsonb_operator: false,
402 },
403 );
404
405 m.insert(
406 "jaccard_distance",
407 OperatorInfo {
408 name: "jaccard_distance",
409 sql_op: "<%>",
410 category: OperatorCategory::Vector,
411 requires_array: false,
412 jsonb_operator: false,
413 },
414 );
415
416 m.insert(
418 "search",
419 OperatorInfo {
420 name: "search",
421 sql_op: "@@",
422 category: OperatorCategory::Fulltext,
423 requires_array: false,
424 jsonb_operator: false,
425 },
426 );
427
428 m.insert(
429 "plainto_tsquery",
430 OperatorInfo {
431 name: "plainto_tsquery",
432 sql_op: "@@",
433 category: OperatorCategory::Fulltext,
434 requires_array: false,
435 jsonb_operator: false,
436 },
437 );
438
439 m.insert(
440 "phraseto_tsquery",
441 OperatorInfo {
442 name: "phraseto_tsquery",
443 sql_op: "@@",
444 category: OperatorCategory::Fulltext,
445 requires_array: false,
446 jsonb_operator: false,
447 },
448 );
449
450 m.insert(
451 "websearch_to_tsquery",
452 OperatorInfo {
453 name: "websearch_to_tsquery",
454 sql_op: "@@",
455 category: OperatorCategory::Fulltext,
456 requires_array: false,
457 jsonb_operator: false,
458 },
459 );
460
461 m.insert(
463 "startswith",
464 OperatorInfo {
465 name: "startswith",
466 sql_op: "LIKE",
467 category: OperatorCategory::String,
468 requires_array: false,
469 jsonb_operator: false,
470 },
471 );
472
473 m.insert(
474 "istartswith",
475 OperatorInfo {
476 name: "istartswith",
477 sql_op: "ILIKE",
478 category: OperatorCategory::String,
479 requires_array: false,
480 jsonb_operator: false,
481 },
482 );
483
484 m.insert(
485 "endswith",
486 OperatorInfo {
487 name: "endswith",
488 sql_op: "LIKE",
489 category: OperatorCategory::String,
490 requires_array: false,
491 jsonb_operator: false,
492 },
493 );
494
495 m.insert(
496 "iendswith",
497 OperatorInfo {
498 name: "iendswith",
499 sql_op: "ILIKE",
500 category: OperatorCategory::String,
501 requires_array: false,
502 jsonb_operator: false,
503 },
504 );
505
506 m.insert(
507 "icontains",
508 OperatorInfo {
509 name: "icontains",
510 sql_op: "ILIKE",
511 category: OperatorCategory::String,
512 requires_array: false,
513 jsonb_operator: false,
514 },
515 );
516
517 m.insert(
518 "imatches",
519 OperatorInfo {
520 name: "imatches",
521 sql_op: "~*",
522 category: OperatorCategory::String,
523 requires_array: false,
524 jsonb_operator: false,
525 },
526 );
527
528 m.insert(
529 "not_matches",
530 OperatorInfo {
531 name: "not_matches",
532 sql_op: "!~",
533 category: OperatorCategory::String,
534 requires_array: false,
535 jsonb_operator: false,
536 },
537 );
538
539 m.insert(
541 "isIPv4",
542 OperatorInfo {
543 name: "isIPv4",
544 sql_op: "family({}) = 4",
545 category: OperatorCategory::Network,
546 requires_array: false,
547 jsonb_operator: false,
548 },
549 );
550
551 m.insert(
552 "isIPv6",
553 OperatorInfo {
554 name: "isIPv6",
555 sql_op: "family({}) = 6",
556 category: OperatorCategory::Network,
557 requires_array: false,
558 jsonb_operator: false,
559 },
560 );
561
562 m.insert(
563 "isPrivate",
564 OperatorInfo {
565 name: "isPrivate",
566 sql_op: "CIDR_RANGE_CHECK",
567 category: OperatorCategory::Network,
568 requires_array: false,
569 jsonb_operator: false,
570 },
571 );
572
573 m.insert(
574 "isLoopback",
575 OperatorInfo {
576 name: "isLoopback",
577 sql_op: "CIDR_RANGE_CHECK",
578 category: OperatorCategory::Network,
579 requires_array: false,
580 jsonb_operator: false,
581 },
582 );
583
584 m.insert(
585 "isMulticast",
586 OperatorInfo {
587 name: "isMulticast",
588 sql_op: "CIDR_RANGE_CHECK",
589 category: OperatorCategory::Network,
590 requires_array: false,
591 jsonb_operator: false,
592 },
593 );
594
595 m.insert(
596 "isLinkLocal",
597 OperatorInfo {
598 name: "isLinkLocal",
599 sql_op: "CIDR_RANGE_CHECK",
600 category: OperatorCategory::Network,
601 requires_array: false,
602 jsonb_operator: false,
603 },
604 );
605
606 m.insert(
607 "isDocumentation",
608 OperatorInfo {
609 name: "isDocumentation",
610 sql_op: "CIDR_RANGE_CHECK",
611 category: OperatorCategory::Network,
612 requires_array: false,
613 jsonb_operator: false,
614 },
615 );
616
617 m.insert(
618 "isCarrierGrade",
619 OperatorInfo {
620 name: "isCarrierGrade",
621 sql_op: "CIDR_RANGE_CHECK",
622 category: OperatorCategory::Network,
623 requires_array: false,
624 jsonb_operator: false,
625 },
626 );
627
628 m.insert(
629 "inSubnet",
630 OperatorInfo {
631 name: "inSubnet",
632 sql_op: "{} <<= {}",
633 category: OperatorCategory::Network,
634 requires_array: false,
635 jsonb_operator: false,
636 },
637 );
638
639 m.insert(
640 "notInSubnet",
641 OperatorInfo {
642 name: "notInSubnet",
643 sql_op: "NOT ({} <<= {})",
644 category: OperatorCategory::Network,
645 requires_array: false,
646 jsonb_operator: false,
647 },
648 );
649
650 m.insert(
651 "subnet_contains",
652 OperatorInfo {
653 name: "subnet_contains",
654 sql_op: ">>",
655 category: OperatorCategory::Network,
656 requires_array: false,
657 jsonb_operator: false,
658 },
659 );
660
661 m.insert(
662 "subnet_overlaps",
663 OperatorInfo {
664 name: "subnet_overlaps",
665 sql_op: "&&",
666 category: OperatorCategory::Network,
667 requires_array: false,
668 jsonb_operator: false,
669 },
670 );
671
672 m.insert(
674 "contains_date",
675 OperatorInfo {
676 name: "contains_date",
677 sql_op: "@>",
678 category: OperatorCategory::DateRange,
679 requires_array: false,
680 jsonb_operator: false,
681 },
682 );
683
684 m.insert(
685 "adjacent",
686 OperatorInfo {
687 name: "adjacent",
688 sql_op: "-|-",
689 category: OperatorCategory::DateRange,
690 requires_array: false,
691 jsonb_operator: false,
692 },
693 );
694
695 m.insert(
696 "strictly_left",
697 OperatorInfo {
698 name: "strictly_left",
699 sql_op: "<<",
700 category: OperatorCategory::DateRange,
701 requires_array: false,
702 jsonb_operator: false,
703 },
704 );
705
706 m.insert(
707 "strictly_right",
708 OperatorInfo {
709 name: "strictly_right",
710 sql_op: ">>",
711 category: OperatorCategory::DateRange,
712 requires_array: false,
713 jsonb_operator: false,
714 },
715 );
716
717 m.insert(
718 "not_left",
719 OperatorInfo {
720 name: "not_left",
721 sql_op: "&>",
722 category: OperatorCategory::DateRange,
723 requires_array: false,
724 jsonb_operator: false,
725 },
726 );
727
728 m.insert(
729 "not_right",
730 OperatorInfo {
731 name: "not_right",
732 sql_op: "&<",
733 category: OperatorCategory::DateRange,
734 requires_array: false,
735 jsonb_operator: false,
736 },
737 );
738
739 m.insert(
740 "overlaps",
741 OperatorInfo {
742 name: "overlaps",
743 sql_op: "&&",
744 category: OperatorCategory::DateRange,
745 requires_array: false,
746 jsonb_operator: false,
747 },
748 );
749
750 m.insert(
752 "ancestor_of",
753 OperatorInfo {
754 name: "ancestor_of",
755 sql_op: "@>",
756 category: OperatorCategory::Ltree,
757 requires_array: false,
758 jsonb_operator: false,
759 },
760 );
761
762 m.insert(
763 "descendant_of",
764 OperatorInfo {
765 name: "descendant_of",
766 sql_op: "<@",
767 category: OperatorCategory::Ltree,
768 requires_array: false,
769 jsonb_operator: false,
770 },
771 );
772
773 m.insert(
774 "matches_lquery",
775 OperatorInfo {
776 name: "matches_lquery",
777 sql_op: "~",
778 category: OperatorCategory::Ltree,
779 requires_array: false,
780 jsonb_operator: false,
781 },
782 );
783
784 m.insert(
785 "matches_ltxtquery",
786 OperatorInfo {
787 name: "matches_ltxtquery",
788 sql_op: "@",
789 category: OperatorCategory::Ltree,
790 requires_array: false,
791 jsonb_operator: false,
792 },
793 );
794
795 m.insert(
796 "matches_any_lquery",
797 OperatorInfo {
798 name: "matches_any_lquery",
799 sql_op: "?",
800 category: OperatorCategory::Ltree,
801 requires_array: true,
802 jsonb_operator: false,
803 },
804 );
805
806 m.insert(
808 "depth_eq",
809 OperatorInfo {
810 name: "depth_eq",
811 sql_op: "nlevel({}) =",
812 category: OperatorCategory::Path,
813 requires_array: false,
814 jsonb_operator: false,
815 },
816 );
817
818 m.insert(
819 "depth_gt",
820 OperatorInfo {
821 name: "depth_gt",
822 sql_op: "nlevel({}) >",
823 category: OperatorCategory::Path,
824 requires_array: false,
825 jsonb_operator: false,
826 },
827 );
828
829 m.insert(
830 "depth_lt",
831 OperatorInfo {
832 name: "depth_lt",
833 sql_op: "nlevel({}) <",
834 category: OperatorCategory::Path,
835 requires_array: false,
836 jsonb_operator: false,
837 },
838 );
839
840 m.insert(
841 "isdescendant",
842 OperatorInfo {
843 name: "isdescendant",
844 sql_op: "<@",
845 category: OperatorCategory::Path,
846 requires_array: false,
847 jsonb_operator: false,
848 },
849 );
850
851 m.insert(
853 "distance_within",
854 OperatorInfo {
855 name: "distance_within",
856 sql_op: "distance_within",
857 category: OperatorCategory::Spatial,
858 requires_array: false,
859 jsonb_operator: false,
860 },
861 );
862
863 m.insert(
865 "strictly_contains",
866 OperatorInfo {
867 name: "strictly_contains",
868 sql_op: "@>",
869 category: OperatorCategory::Containment,
870 requires_array: false,
871 jsonb_operator: true,
872 },
873 );
874
875 m.insert(
877 "neq",
878 OperatorInfo {
879 name: "neq",
880 sql_op: "!=",
881 category: OperatorCategory::Comparison,
882 requires_array: false,
883 jsonb_operator: false,
884 },
885 );
886
887 m.insert(
888 "isnull",
889 OperatorInfo {
890 name: "isnull",
891 sql_op: "IS NULL",
892 category: OperatorCategory::Null,
893 requires_array: false,
894 jsonb_operator: false,
895 },
896 );
897
898 m.insert(
899 "array_eq",
900 OperatorInfo {
901 name: "array_eq",
902 sql_op: "=",
903 category: OperatorCategory::Array,
904 requires_array: false,
905 jsonb_operator: false,
906 },
907 );
908
909 m.insert(
910 "array_neq",
911 OperatorInfo {
912 name: "array_neq",
913 sql_op: "!=",
914 category: OperatorCategory::Array,
915 requires_array: false,
916 jsonb_operator: false,
917 },
918 );
919
920 m.insert(
921 "array_contained_by",
922 OperatorInfo {
923 name: "array_contained_by",
924 sql_op: "<@",
925 category: OperatorCategory::Array,
926 requires_array: false,
927 jsonb_operator: false,
928 },
929 );
930
931 m.insert(
932 "notin",
933 OperatorInfo {
934 name: "notin",
935 sql_op: "NOT IN",
936 category: OperatorCategory::Comparison,
937 requires_array: true,
938 jsonb_operator: false,
939 },
940 );
941
942 m
943});
944
945#[must_use]
955pub fn get_operator_info(name: &str) -> Option<&'static OperatorInfo> {
956 OPERATOR_REGISTRY.get(name)
957}
958
959#[must_use]
970pub fn is_operator(name: &str) -> bool {
971 OPERATOR_REGISTRY.contains_key(name)
972}
973
974#[must_use]
984pub fn get_operators_by_category(category: OperatorCategory) -> Vec<&'static OperatorInfo> {
985 OPERATOR_REGISTRY.values().filter(|op| op.category == category).collect()
986}