Skip to content

Commit

Permalink
Merge pull request #38 from box/search
Browse files Browse the repository at this point in the history
Improve search endpoint.
  • Loading branch information
Jeff-Meadows committed Apr 1, 2015
2 parents ef5ca62 + 4285510 commit e5243da
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 25 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
language: python
python: 2.7
env:
- TOX_ENV=py26
- TOX_ENV=py27
Expand Down
6 changes: 6 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
Release History
---------------

1.1.4 (2015-04-01)
++++++++++++++++++

- Added support to the search endpoint for metadata filters.
- Added support to the search endpoint for filtering based on result type and content types.

1.1.3 (2015-03-26)
++++++++++++++++++

Expand Down
36 changes: 28 additions & 8 deletions boxsdk/object/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,18 @@ def make_single_metadata_filter(template_key, scope):
"""
return MetadataSearchFilter(template_key, scope)

def search(self, query, limit, offset, ancestor_folders=None, file_extensions=None, metadata_filters=None):
def search(
self,
query,
limit=100,
offset=0,
ancestor_folders=None,
file_extensions=None,
metadata_filters=None,
result_type=None,
content_types=None,
**kwargs
):
"""
Search Box for items matching the given query.
Expand All @@ -174,7 +185,7 @@ def search(self, query, limit, offset, ancestor_folders=None, file_extensions=No
:param ancestor_folders:
Folder ids to limit the search to.
:type ancestor_folders:
`iterable` of :class:`Folder`
`Iterable` of :class:`Folder`
:param file_extensions:
File extensions to limit the search to.
:type file_extensions:
Expand All @@ -183,6 +194,14 @@ def search(self, query, limit, offset, ancestor_folders=None, file_extensions=No
Filters used for metadata search
:type metadata_filters:
:class:`MetadataSearchFilters`
:param result_type:
Which type of result you want. Can be file or folder.
:type result_type:
`unicode`
:param content_types:
Which content types to search. Valid types include name, description, file_content, comments, and tags.
:type content_types:
`Iterable` of `unicode`
:return:
A list of items that match the search query.
:rtype:
Expand All @@ -199,13 +218,14 @@ def search(self, query, limit, offset, ancestor_folders=None, file_extensions=No
'ancestor_folder_ids': ','.join([folder.object_id for folder in ancestor_folders])
})
if file_extensions:
params.update({
'file_extensions': ','.join(file_extensions)
})
params.update({'file_extensions': ','.join(file_extensions)})
if metadata_filters:
params.update({
'mdfilters': json.dumps(metadata_filters.as_list())
})
params.update({'mdfilters': json.dumps(metadata_filters.as_list())})
if content_types:
params.update({'content_types': ','.join(content_types)})
if result_type:
params.update({'type': result_type})
params.update(kwargs)
box_response = self._session.get(url, params=params)
response = box_response.json()
return [Translator().translate(item['type'])(self._session, item['id'], item) for item in response['entries']]
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def main():
install_requires.append('ordereddict>=1.1')
setup(
name='boxsdk',
version='1.1.3',
version='1.1.4',
description='Official Box Python SDK',
long_description=open(join(base_dir, 'README.rst')).read(),
author='Box',
Expand Down
66 changes: 50 additions & 16 deletions test/unit/object/test_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,24 @@ def search_query():
return 'myquery'


@pytest.fixture
def search_limit():
return 20
@pytest.fixture(params=(1, 20, 100))
def search_limit(request):
return request.param


@pytest.fixture
def search_offset():
return 0
@pytest.fixture(params=(0, 10))
def search_offset(request):
return request.param


@pytest.fixture(params=(None, 'file', 'folder'))
def search_result_type(request):
return request.param


@pytest.fixture(params=(None, ('name',), ('name', 'description')))
def search_content_types(request):
return request.param


@pytest.fixture
Expand Down Expand Up @@ -81,6 +91,8 @@ def compare_params(self, other):
if json.loads(self['mdfilters']) != json.loads(other['mdfilters']):
return False
# For other keys, just ensure that they are equal
elif key in ('type', 'content_types'):
return self[key] is None or self[key] == other[key]
else:
if self[key] != other[key]:
return False
Expand All @@ -96,20 +108,31 @@ def test_search_with_value_based_filters(
search_offset,
search_value_based_filters,
search_response,
search_entries
search_entries,
search_result_type,
search_content_types,
):
# pylint:disable=redefined-outer-name
mock_box_session.get.return_value, _ = make_mock_box_request(response=search_response)
response = test_search.search(search_query, limit=search_limit, offset=search_offset, metadata_filters=search_value_based_filters)
response = test_search.search(
search_query,
limit=search_limit,
offset=search_offset,
metadata_filters=search_value_based_filters,
result_type=search_result_type,
content_types=search_content_types,
)
assert response == [File(mock_box_session, search_entry['id'], search_entry) for search_entry in search_entries]

mock_box_session.get.assert_called_once_with(
test_search.get_url(),
params=Matcher(compare_params, {
'query': 'myquery',
'limit': 20,
'query': search_query,
'limit': search_limit,
'mdfilters': json.dumps(search_value_based_filters.as_list()),
'offset': 0
'offset': search_offset,
'type': search_result_type,
'content_types': ','.join(search_content_types) if search_content_types else search_content_types,
})
)

Expand All @@ -123,20 +146,31 @@ def test_search_with_range_filters(
search_offset,
search_range_filters,
search_response,
search_entries
search_entries,
search_result_type,
search_content_types,
):
# pylint:disable=redefined-outer-name
mock_box_session.get.return_value, _ = make_mock_box_request(response=search_response)
response = test_search.search(search_query, limit=search_limit, offset=search_offset, metadata_filters=search_range_filters)
response = test_search.search(
search_query,
limit=search_limit,
offset=search_offset,
metadata_filters=search_range_filters,
result_type=search_result_type,
content_types=search_content_types,
)
assert response == [File(mock_box_session, search_entry['id'], search_entry) for search_entry in search_entries]

mock_box_session.get.assert_called_once_with(
test_search.get_url(),
params=Matcher(compare_params, {
'query': 'myquery',
'limit': 20,
'query': search_query,
'limit': search_limit,
'mdfilters': json.dumps(search_range_filters.as_list()),
'offset': 0
'offset': search_offset,
'type': search_result_type,
'content_types': ','.join(search_content_types) if search_content_types else search_content_types,
})
)

Expand Down

0 comments on commit e5243da

Please sign in to comment.