@@ -8,6 +8,12 @@ local json = require('json')
8
8
local avro_schema = require (' avro_schema' )
9
9
local utils = require (' graphql.utils' )
10
10
local clock = require (' clock' )
11
+ local rex = utils .optional_require (' rex_pcre' )
12
+
13
+ -- XXX: consider using [1] when it will be mature enough;
14
+ -- look into [2] for the status.
15
+ -- [1]: https://github.com/igormunkin/lua-re
16
+ -- [2]: https://github.com/tarantool/tarantool/issues/2764
11
17
12
18
local accessor_general = {}
13
19
local DEF_RESULTING_OBJECT_CNT_MAX = 10000
@@ -696,6 +702,25 @@ local function validate_collections(collections, schemas)
696
702
end
697
703
end
698
704
705
+ --- XXX
706
+ local function match_using_re (obj , pcre )
707
+ if pcre == nil then return true end
708
+
709
+ for field_name , re in pairs (pcre ) do
710
+ -- skip an object with null in a string* field
711
+ if obj [field_name ] == nil then
712
+ return false
713
+ end
714
+ -- XXX: compile re once
715
+ local re = rex .new (re )
716
+ if not re :match (obj [field_name ]) then
717
+ return false
718
+ end
719
+ end
720
+
721
+ return true
722
+ end
723
+
699
724
--- Perform unflatten, skipping, filtering, limiting of objects. This is the
700
725
--- core of the `select_internal` function.
701
726
---
@@ -741,6 +766,8 @@ local function process_tuple(state, tuple, opts)
741
766
qstats .fetched_object_cnt , fetched_object_cnt_max ))
742
767
assert (qcontext .deadline_clock > clock .monotonic64 (),
743
768
' query execution timeout exceeded, use `timeout_ms` to increase it' )
769
+ local collection_name = opts .collection_name
770
+ local pcre = opts .pcre
744
771
745
772
-- convert tuple -> object
746
773
local obj = opts .unflatten_tuple (opts .collection_name , tuple ,
@@ -755,7 +782,8 @@ local function process_tuple(state, tuple, opts)
755
782
end
756
783
757
784
-- filter out non-matching objects
758
- local match = utils .is_subtable (obj , filter )
785
+ local match = utils .is_subtable (obj , filter ) and
786
+ match_using_re (obj , pcre )
759
787
if do_filter then
760
788
if not match then return true end
761
789
else
@@ -828,6 +856,8 @@ local function select_internal(self, collection_name, from, filter, args, extra)
828
856
-- XXX: save type at parsing and check here
829
857
-- assert(args.offset == nil or type(args.offset) == 'number',
830
858
-- 'args.offset must be a number of nil, got ' .. type(args.offset))
859
+ assert (args .pcre == nil or type (args .pcre ) == ' table' ,
860
+ ' args.pcre must be nil or a table, got ' .. type (args .pcre ))
831
861
832
862
local collection = self .collections [collection_name ]
833
863
assert (collection ~= nil ,
@@ -876,6 +906,7 @@ local function select_internal(self, collection_name, from, filter, args, extra)
876
906
collection_name = collection_name ,
877
907
unflatten_tuple = self .funcs .unflatten_tuple ,
878
908
default_unflatten_tuple = default_unflatten_tuple ,
909
+ pcre = args .pcre ,
879
910
}
880
911
881
912
if index == nil then
@@ -976,6 +1007,87 @@ local function init_qcontext(accessor, qcontext)
976
1007
settings .timeout_ms * 1000 * 1000
977
1008
end
978
1009
1010
+ --- XXX
1011
+ local function get_primary_key_type (self , collection_name )
1012
+ -- get name of field of primary key
1013
+ local _ , index_meta = get_primary_index_meta (
1014
+ self , collection_name )
1015
+
1016
+ local offset_fields = {}
1017
+
1018
+ for _ , field_name in ipairs (index_meta .fields ) do
1019
+ local field_type
1020
+ local collection = self .collections [collection_name ]
1021
+ local schema = self .schemas [collection .schema_name ]
1022
+ for _ , field in ipairs (schema .fields ) do
1023
+ if field .name == field_name then
1024
+ field_type = field .type
1025
+ end
1026
+ end
1027
+ assert (field_type ~= nil ,
1028
+ (' cannot find type for primary index field "%s" ' ..
1029
+ ' for collection "%s"' ):format (field_name ,
1030
+ collection_name ))
1031
+ assert (type (field_type ) == ' string' ,
1032
+ ' field type must be a string, got ' ..
1033
+ type (field_type ))
1034
+ offset_fields [# offset_fields + 1 ] = {
1035
+ name = field_name ,
1036
+ type = field_type ,
1037
+ }
1038
+ end
1039
+
1040
+ local offset_type
1041
+ assert (# offset_fields > 0 ,
1042
+ ' offset must contain at least one field' )
1043
+ if # offset_fields == 1 then
1044
+ -- use a scalar type
1045
+ offset_type = offset_fields [1 ].type
1046
+ else
1047
+ -- construct an input type
1048
+ offset_type = {
1049
+ name = collection_name .. ' _offset' ,
1050
+ type = ' record' ,
1051
+ fields = offset_fields ,
1052
+ }
1053
+ end
1054
+
1055
+ return offset_type
1056
+ end
1057
+
1058
+ -- XXX: add string fields of a nested record / 1:1 connection
1059
+
1060
+ --- XXX
1061
+ --- Note: it is only for list of objects and is not for destination of 1:1
1062
+ --- connection
1063
+ local function get_pcre_argument_type (self , collection_name )
1064
+ local collection = self .collections [collection_name ]
1065
+ assert (collection ~= nil , ' cannot found collection ' ..
1066
+ tostring (collection_name ))
1067
+ local schema = self .schemas [collection .schema_name ]
1068
+ assert (schema ~= nil , ' cannot found schema ' ..
1069
+ tostring (collection .schema_name ))
1070
+
1071
+ assert (schema .type == ' record' ,
1072
+ ' top-level object expected to be a record, got ' ..
1073
+ tostring (schema .type ))
1074
+
1075
+ local string_fields = {}
1076
+
1077
+ for _ , field in ipairs (schema .fields ) do
1078
+ if field .type == ' string' or field .type == ' string*' then
1079
+ string_fields [# string_fields + 1 ] = table .copy (field )
1080
+ end
1081
+ end
1082
+
1083
+ local pcre_type = {
1084
+ name = collection_name .. ' _pcre' ,
1085
+ type = ' record' ,
1086
+ fields = string_fields ,
1087
+ }
1088
+ return pcre_type
1089
+ end
1090
+
979
1091
--- Create a new data accessor.
980
1092
---
981
1093
--- Provided `funcs` argument determines certain functions for retrieving
@@ -1114,53 +1226,20 @@ function accessor_general.new(opts, funcs)
1114
1226
args , extra )
1115
1227
end ,
1116
1228
list_args = function (self , collection_name )
1117
- -- get name of field of primary key
1118
- local _ , index_meta = get_primary_index_meta (
1119
- self , collection_name )
1120
-
1121
- local offset_fields = {}
1122
-
1123
- for _ , field_name in ipairs (index_meta .fields ) do
1124
- local field_type
1125
- local collection = self .collections [collection_name ]
1126
- local schema = self .schemas [collection .schema_name ]
1127
- for _ , field in ipairs (schema .fields ) do
1128
- if field .name == field_name then
1129
- field_type = field .type
1130
- end
1131
- end
1132
- assert (field_type ~= nil ,
1133
- (' cannot find type for primary index field "%s" ' ..
1134
- ' for collection "%s"' ):format (field_name ,
1135
- collection_name ))
1136
- assert (type (field_type ) == ' string' ,
1137
- ' field type must be a string, got ' ..
1138
- type (field_type ))
1139
- offset_fields [# offset_fields + 1 ] = {
1140
- name = field_name ,
1141
- type = field_type ,
1142
- }
1143
- end
1229
+ local offset_type = get_primary_key_type (self , collection_name )
1144
1230
1145
- local offset_type
1146
- assert (# offset_fields > 0 ,
1147
- ' offset must contain at least one field' )
1148
- if # offset_fields == 1 then
1149
- -- use a scalar type
1150
- offset_type = offset_fields [1 ].type
1151
- else
1152
- -- construct an input type
1153
- offset_type = {
1154
- name = collection_name .. ' _offset' ,
1155
- type = ' record' ,
1156
- fields = offset_fields ,
1157
- }
1231
+ -- add `pcre` argument only if lrexlib-pcre was found
1232
+ local pcre_field
1233
+ if rex ~= nil then
1234
+ local pcre_type = get_pcre_argument_type (self , collection_name )
1235
+ pcre_field = {name = ' pcre' , type = pcre_type }
1158
1236
end
1159
1237
1160
1238
return {
1161
1239
{name = ' limit' , type = ' int' },
1162
1240
{name = ' offset' , type = offset_type },
1163
1241
-- {name = 'filter', type = ...},
1242
+ pcre_field ,
1164
1243
}
1165
1244
end ,
1166
1245
}
0 commit comments