Skip to content

Commit b5cff5e

Browse files
committed
Implement exact string search in REST API
Now with ':=' instead of '=' an exact string match is requested. In addition we now support '~=' for a substring search. The old semantics of '=' is kept. The new syntax works for all types of properties but only makes a difference for String properties. Note that this is not yet documented, pending discussion if we want to keep this syntax.
1 parent 53bb889 commit b5cff5e

File tree

2 files changed

+47
-5
lines changed

2 files changed

+47
-5
lines changed

roundup/rest.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,7 @@ def get_collection(self, class_name, input):
644644

645645
# Handle filtering and pagination
646646
filter_props = {}
647+
exact_props = {}
647648
page = {
648649
'size': None,
649650
'index': 1 # setting just size starts at page 1
@@ -696,6 +697,12 @@ def get_collection(self, class_name, input):
696697
# like @apiver
697698
pass
698699
else: # serve the filter purpose
700+
exact = False
701+
if key.endswith (':') :
702+
exact = True
703+
key = key [:-1]
704+
elif key.endswith ('~') :
705+
key = key [:-1]
699706
p = key.split('.', 1)[0]
700707
try:
701708
prop = class_obj.getprops()[p]
@@ -731,17 +738,24 @@ def get_collection(self, class_name, input):
731738
vals.append(linkcls.lookup(p))
732739
filter_props[key] = vals
733740
else:
734-
if key in filter_props:
735-
if isinstance(filter_props[key], list):
736-
filter_props[key].append(value)
741+
if not isinstance (prop, hyperdb.String):
742+
exact = False
743+
props = filter_props
744+
if exact:
745+
props = exact_props
746+
if key in props:
747+
if isinstance(props[key], list):
748+
props[key].append(value)
737749
else:
738-
filter_props[key]=[filter_props[key],value]
750+
props[key] = [props[key],value]
739751
else:
740-
filter_props[key] = value
752+
props[key] = value
741753
l = [filter_props]
742754
kw = {}
743755
if sort:
744756
l.append(sort)
757+
if exact_props:
758+
kw ['exact_match_spec'] = exact_props
745759
if page ['size'] is not None and page ['size'] > 0:
746760
kw ['limit'] = page ['size']
747761
if page ['index'] is not None and page ['index'] > 1:

test/rest_common.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,34 @@ def testGetTransitive(self):
241241
results = self.server.get_collection('issue', form)
242242
self.assertDictEqual(expected, results)
243243

244+
def testGetExactMatch(self):
245+
""" Retrieve all issues with an exact title
246+
"""
247+
base_path = self.db.config['TRACKER_WEB'] + 'rest/data/'
248+
#self.maxDiff=None
249+
self.create_sampledata()
250+
self.db.issue.set('2', title='This is an exact match')
251+
self.db.issue.set('3', title='This is an exact match')
252+
self.db.issue.set('1', title='This is AN exact match')
253+
expected={'data':
254+
{'@total_size': 2,
255+
'collection': [
256+
{ 'id': '2',
257+
'link': base_path + 'issue/2',
258+
},
259+
{ 'id': '3',
260+
'link': base_path + 'issue/3',
261+
},
262+
]}
263+
}
264+
form = cgi.FieldStorage()
265+
form.list = [
266+
cgi.MiniFieldStorage('title:', 'This is an exact match'),
267+
cgi.MiniFieldStorage('@sort', 'status.name'),
268+
]
269+
results = self.server.get_collection('issue', form)
270+
self.assertDictEqual(expected, results)
271+
244272
def testOutputFormat(self):
245273
""" test of @fields and @verbose implementation """
246274

0 commit comments

Comments
 (0)