Skip to content

Commit e5243da

Browse files
committed
Merge pull request #38 from box/search
Improve search endpoint.
2 parents ef5ca62 + 4285510 commit e5243da

File tree

5 files changed

+86
-25
lines changed

5 files changed

+86
-25
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
language: python
2+
python: 2.7
23
env:
34
- TOX_ENV=py26
45
- TOX_ENV=py27

HISTORY.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
Release History
44
---------------
55

6+
1.1.4 (2015-04-01)
7+
++++++++++++++++++
8+
9+
- Added support to the search endpoint for metadata filters.
10+
- Added support to the search endpoint for filtering based on result type and content types.
11+
612
1.1.3 (2015-03-26)
713
++++++++++++++++++
814

boxsdk/object/search.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,18 @@ def make_single_metadata_filter(template_key, scope):
155155
"""
156156
return MetadataSearchFilter(template_key, scope)
157157

158-
def search(self, query, limit, offset, ancestor_folders=None, file_extensions=None, metadata_filters=None):
158+
def search(
159+
self,
160+
query,
161+
limit=100,
162+
offset=0,
163+
ancestor_folders=None,
164+
file_extensions=None,
165+
metadata_filters=None,
166+
result_type=None,
167+
content_types=None,
168+
**kwargs
169+
):
159170
"""
160171
Search Box for items matching the given query.
161172
@@ -174,7 +185,7 @@ def search(self, query, limit, offset, ancestor_folders=None, file_extensions=No
174185
:param ancestor_folders:
175186
Folder ids to limit the search to.
176187
:type ancestor_folders:
177-
`iterable` of :class:`Folder`
188+
`Iterable` of :class:`Folder`
178189
:param file_extensions:
179190
File extensions to limit the search to.
180191
:type file_extensions:
@@ -183,6 +194,14 @@ def search(self, query, limit, offset, ancestor_folders=None, file_extensions=No
183194
Filters used for metadata search
184195
:type metadata_filters:
185196
:class:`MetadataSearchFilters`
197+
:param result_type:
198+
Which type of result you want. Can be file or folder.
199+
:type result_type:
200+
`unicode`
201+
:param content_types:
202+
Which content types to search. Valid types include name, description, file_content, comments, and tags.
203+
:type content_types:
204+
`Iterable` of `unicode`
186205
:return:
187206
A list of items that match the search query.
188207
:rtype:
@@ -199,13 +218,14 @@ def search(self, query, limit, offset, ancestor_folders=None, file_extensions=No
199218
'ancestor_folder_ids': ','.join([folder.object_id for folder in ancestor_folders])
200219
})
201220
if file_extensions:
202-
params.update({
203-
'file_extensions': ','.join(file_extensions)
204-
})
221+
params.update({'file_extensions': ','.join(file_extensions)})
205222
if metadata_filters:
206-
params.update({
207-
'mdfilters': json.dumps(metadata_filters.as_list())
208-
})
223+
params.update({'mdfilters': json.dumps(metadata_filters.as_list())})
224+
if content_types:
225+
params.update({'content_types': ','.join(content_types)})
226+
if result_type:
227+
params.update({'type': result_type})
228+
params.update(kwargs)
209229
box_response = self._session.get(url, params=params)
210230
response = box_response.json()
211231
return [Translator().translate(item['type'])(self._session, item['id'], item) for item in response['entries']]

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def main():
5757
install_requires.append('ordereddict>=1.1')
5858
setup(
5959
name='boxsdk',
60-
version='1.1.3',
60+
version='1.1.4',
6161
description='Official Box Python SDK',
6262
long_description=open(join(base_dir, 'README.rst')).read(),
6363
author='Box',

test/unit/object/test_search.py

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,24 @@ def search_query():
1212
return 'myquery'
1313

1414

15-
@pytest.fixture
16-
def search_limit():
17-
return 20
15+
@pytest.fixture(params=(1, 20, 100))
16+
def search_limit(request):
17+
return request.param
1818

1919

20-
@pytest.fixture
21-
def search_offset():
22-
return 0
20+
@pytest.fixture(params=(0, 10))
21+
def search_offset(request):
22+
return request.param
23+
24+
25+
@pytest.fixture(params=(None, 'file', 'folder'))
26+
def search_result_type(request):
27+
return request.param
28+
29+
30+
@pytest.fixture(params=(None, ('name',), ('name', 'description')))
31+
def search_content_types(request):
32+
return request.param
2333

2434

2535
@pytest.fixture
@@ -81,6 +91,8 @@ def compare_params(self, other):
8191
if json.loads(self['mdfilters']) != json.loads(other['mdfilters']):
8292
return False
8393
# For other keys, just ensure that they are equal
94+
elif key in ('type', 'content_types'):
95+
return self[key] is None or self[key] == other[key]
8496
else:
8597
if self[key] != other[key]:
8698
return False
@@ -96,20 +108,31 @@ def test_search_with_value_based_filters(
96108
search_offset,
97109
search_value_based_filters,
98110
search_response,
99-
search_entries
111+
search_entries,
112+
search_result_type,
113+
search_content_types,
100114
):
101115
# pylint:disable=redefined-outer-name
102116
mock_box_session.get.return_value, _ = make_mock_box_request(response=search_response)
103-
response = test_search.search(search_query, limit=search_limit, offset=search_offset, metadata_filters=search_value_based_filters)
117+
response = test_search.search(
118+
search_query,
119+
limit=search_limit,
120+
offset=search_offset,
121+
metadata_filters=search_value_based_filters,
122+
result_type=search_result_type,
123+
content_types=search_content_types,
124+
)
104125
assert response == [File(mock_box_session, search_entry['id'], search_entry) for search_entry in search_entries]
105126

106127
mock_box_session.get.assert_called_once_with(
107128
test_search.get_url(),
108129
params=Matcher(compare_params, {
109-
'query': 'myquery',
110-
'limit': 20,
130+
'query': search_query,
131+
'limit': search_limit,
111132
'mdfilters': json.dumps(search_value_based_filters.as_list()),
112-
'offset': 0
133+
'offset': search_offset,
134+
'type': search_result_type,
135+
'content_types': ','.join(search_content_types) if search_content_types else search_content_types,
113136
})
114137
)
115138

@@ -123,20 +146,31 @@ def test_search_with_range_filters(
123146
search_offset,
124147
search_range_filters,
125148
search_response,
126-
search_entries
149+
search_entries,
150+
search_result_type,
151+
search_content_types,
127152
):
128153
# pylint:disable=redefined-outer-name
129154
mock_box_session.get.return_value, _ = make_mock_box_request(response=search_response)
130-
response = test_search.search(search_query, limit=search_limit, offset=search_offset, metadata_filters=search_range_filters)
155+
response = test_search.search(
156+
search_query,
157+
limit=search_limit,
158+
offset=search_offset,
159+
metadata_filters=search_range_filters,
160+
result_type=search_result_type,
161+
content_types=search_content_types,
162+
)
131163
assert response == [File(mock_box_session, search_entry['id'], search_entry) for search_entry in search_entries]
132164

133165
mock_box_session.get.assert_called_once_with(
134166
test_search.get_url(),
135167
params=Matcher(compare_params, {
136-
'query': 'myquery',
137-
'limit': 20,
168+
'query': search_query,
169+
'limit': search_limit,
138170
'mdfilters': json.dumps(search_range_filters.as_list()),
139-
'offset': 0
171+
'offset': search_offset,
172+
'type': search_result_type,
173+
'content_types': ','.join(search_content_types) if search_content_types else search_content_types,
140174
})
141175
)
142176

0 commit comments

Comments
 (0)