def setUp(self): self.config = SearchConfig('foo', 'bar') requester = Requester() self.transporter = Transporter(requester, self.config) self.transporter.read = mock.Mock(name="read") self.transporter.read.return_value = {} self.transporter.write = mock.Mock(name="write") self.transporter.write.return_value = {} self.index = SearchIndex(self.transporter, self.config, 'index-name')
def __init__(self, search_index, transporter, config, name): # type: (SearchIndex, TransporterAsync, SearchConfig, str) -> None self._search_index = search_index self._transporter_async = transporter super(SearchIndexAsync, self).__init__(search_index._transporter, config, name) search_index = SearchIndex(transporter, config, name) search_index.__setattr__('_sync', self._sync) _create_async_methods_in(self, search_index)
def __init__(self, search_index, transporter, config, name): # type: (SearchIndex, TransporterAsync, SearchConfig, str) -> None self._search_index = search_index self._transporter_async = transporter super(SearchIndexAsync, self).__init__( search_index._transporter, config, name ) search_index = SearchIndex(transporter, config, name) search_index.__setattr__('_sync', self._sync) _create_async_methods_in(self, search_index)
async def search_for_url(cls, *, search_index: SearchIndex, base_url: str) -> AlgoliaMetadata: """Create an AlgoliaMetadata from a base documentation url.""" request_options = { "filters": f"baseUrl:{cls.escape_facet_value(base_url)}", "attributesToRetrieve": [ "handle", "h1", "description", "series", "contentType", ], } results = search_index.search("", request_options) if len(results["hits"]) == 0: raise ValueError(f"Algolia record unavailable for {base_url}") hit = results["hits"][0] return cls( title=hit["h1"], description=hit["description"], series=hit["series"], handle=hit["handle"], content_type=hit["contentType"], )
def list_remote_keys(index: SearchIndex) -> List[str]: """handle pagination eventually...""" all_object_ids: Set[str] = set() params = {"attributesToRetrieve": "objectID", "hitsPerPage": 100} first_page = index.search("", params) first_page_hits = hit_object_ids(first_page) all_object_ids.update(first_page_hits) page_count = first_page["nbPages"] for i in range(1, page_count): next_page = index.search("", dict(params, page=i)) if next_page["nbHits"] <= 0: break next_page_hits = hit_object_ids(next_page["hits"]) all_object_ids.update(next_page_hits) return list(all_object_ids)
def setUp(self): self.config = SearchConfig('foo', 'bar') requester = Requester() self.transporter = Transporter(requester, self.config) self.transporter.read = mock.Mock(name="read") self.transporter.read.return_value = {} self.transporter.write = mock.Mock(name="write") self.transporter.write.return_value = {} self.index = SearchIndex(self.transporter, self.config, 'index-name')
async def search_for_old_records( *, index: SearchIndex, base_url: str, surrogate_key: str) -> AsyncIterator[Dict[str, Any]]: filters = (f"baseUrl:{escape_facet_value(base_url)}" " AND NOT " f"surrogateKey:{escape_facet_value(surrogate_key)}") async for result in index.browse_objects_async({ "filters": filters, "attributesToRetrieve": ["baseUrl", "surrogateKey"], "attributesToHighlight": [], }): yield result
def test_search(self): responses = MultipleResponse() responses.push(self.index.save_objects([ {"company": "Algolia", "name": "Julien Lemoine", "objectID": "julien-lemoine"}, # noqa: E501 {"company": "Algolia", "name": "Nicolas Dessaigne", "objectID": "nicolas-dessaigne"}, # noqa: E501 {"company": "Amazon", "name": "Jeff Bezos"}, {"company": "Apple", "name": "Steve Jobs"}, {"company": "Apple", "name": "Steve Wozniak"}, {"company": "Arista Networks", "name": "Jayshree Ullal"}, {"company": "Google", "name": "Larry Page"}, {"company": "Google", "name": "Rob Pike"}, {"company": "Google", "name": "Serguey Brin"}, {"company": "Microsoft", "name": "Bill Gates"}, {"company": "SpaceX", "name": "Elon Musk"}, {"company": "Tesla", "name": "Elon Musk"}, {"company": "Yahoo", "name": "Marissa Mayer"} ], {'autoGenerateObjectIDIfNotExist': True})) responses.push(self.index.set_settings({ 'attributesForFaceting': ["searchable(company)"] })) responses.wait() # Perform a search query using search with the query `algolia` and no # parameter and check that the number of returned hits is equal to 2 result = self.index.search('algolia') self.assertEqual(result['nbHits'], 2) self.assertEqual(SearchIndex.get_object_position( result, 'nicolas-dessaigne'), 0 ) self.assertEqual(SearchIndex.get_object_position( result, 'julien-lemoine'), 1 ) self.assertEqual(SearchIndex.get_object_position(result, ''), -1) # Call find_object with the following parameters and check that # no object is found with self.assertRaises(ObjectNotFoundException): self.index.find_object(lambda _: False) # Call find_object with the following parameters and check that # the first object is returned with a `position=0` and `page=0` found = self.index.find_object(lambda _: True) self.assertEqual(found['position'], 0) self.assertEqual(found['page'], 0) def callback(obj): # type: (dict) -> bool return obj.get('company') == 'Apple' # Call find_object with the following parameters and check that # no object is found with self.assertRaises(ObjectNotFoundException): self.index.find_object(callback, { 'query': 'algolia' }) # Call find_object with the following parameters and check that # no object is found with self.assertRaises(ObjectNotFoundException): self.index.find_object(callback, { 'query': '', 'paginate': False, 'hitsPerPage': 5 }) # Call find_object with the following parameters and check that # the first object is returned with a `position=0` and `page=2` found = self.index.find_object(callback, { 'query': '', 'paginate': True, 'hitsPerPage': 5 }) self.assertEqual(found['position'], 0) self.assertEqual(found['page'], 2) # Perform a search using search with the query `elon` and the # following parameter and check that the queryID field from # the response is not empty result = self.index.search('elon', {'clickAnalytics': True}) self.assertIn('queryID', result) # Perform a faceted search using search with the query `elon` and the # following parameters and check that the number of returned hits is # equal to 1 result = self.index.search('elon', { 'facets': '*', 'facetFilters': 'company:tesla' }) self.assertEqual(result['nbHits'], 1) # Perform a filtered search using search with the query `elon` and the # following parameters and check that the number of returned hits is # equal to 2 result = self.index.search('elon', { 'facets': '*', 'filters': '(company:tesla OR company:spacex)' }) self.assertEqual(result['nbHits'], 2) result = self.index.search_for_facet_values('company', 'a')[ 'facetHits'] values = list( map(lambda facet: facet['value'], result)) self.assertIn('Algolia', values) self.assertIn('Amazon', values) self.assertIn('Apple', values) self.assertIn('Arista Networks', values)
def init_index(self, name): # type: (str) -> SearchIndex return SearchIndex(self._transporter, self._config, name)
class TestSearchIndex(unittest.TestCase): def setUp(self): self.config = SearchConfig('foo', 'bar') requester = Requester() self.transporter = Transporter(requester, self.config) self.transporter.read = mock.Mock(name="read") self.transporter.read.return_value = {} self.transporter.write = mock.Mock(name="write") self.transporter.write.return_value = {} self.index = SearchIndex(self.transporter, self.config, 'index-name') def test_app_id_getter(self): self.assertEqual(self.index.app_id, 'foo') def test_name_getter(self): self.assertEqual(self.index.name, 'index-name') def test_save_objects(self): # Saving an object without object id with self.assertRaises(MissingObjectIdException) as _: self.index.save_objects([{'foo': 'bar'}]) with self.assertRaises(MissingObjectIdException) as _: self.index.save_objects([{'foo': 'bar'}], {'autoGenerateObjectIDIfNotExist': False}) self.index.save_objects([{'foo': 'bar'}], {'autoGenerateObjectIDIfNotExist': True}) self.transporter.write.assert_called_once_with( 'POST', '1/indexes/index-name/batch', {'requests': [{'action': 'addObject', 'body': {'foo': 'bar'}}]}, {}, ) self.transporter.write = mock.Mock(name="write") self.index.save_objects([{'foo': 'bar', 'objectID': 'foo'}]) self.transporter.write.assert_called_once_with( 'POST', '1/indexes/index-name/batch', { 'requests': [ { 'action': 'updateObject', 'body': {'foo': 'bar', 'objectID': 'foo'}} ] }, None ) def test_partial_update_objects(self): # Saving an object without object id with self.assertRaises(MissingObjectIdException) as _: self.index.partial_update_objects([{'foo': 'bar'}]) with self.assertRaises(MissingObjectIdException) as _: self.index.partial_update_objects([{'foo': 'bar'}], {'createIfNotExists': False}) self.index.partial_update_objects([{'foo': 'bar'}], {'createIfNotExists': True}) self.transporter.write.assert_called_once_with( 'POST', '1/indexes/index-name/batch', {'requests': [ {'action': 'partialUpdateObject', 'body': {'foo': 'bar'}}]}, {}, ) self.transporter.write = mock.Mock(name="write") self.index.partial_update_objects([{'foo': 'bar', 'objectID': 'foo'}]) self.transporter.write.assert_called_once_with( 'POST', '1/indexes/index-name/batch', { 'requests': [ { 'action': 'partialUpdateObjectNoCreate', 'body': {'foo': 'bar', 'objectID': 'foo'} } ] }, None, ) def test_get_objects(self): request_options = RequestOptions.create(self.config) requests = [{ 'indexName': 'index-name', 'objectID': 'foo_id' }] self.index.get_objects(['foo_id'], request_options) self.transporter.read.assert_called_once_with( 'POST', '1/indexes/*/objects', {'requests': requests}, # asserts version 2 it's used. request_options ) def test_get_objects_with_attributes_to_retreive(self): request_options = RequestOptions.create(self.config, { 'attributesToRetrieve': ['firstname', 'lastname'] }) requests = [{ 'indexName': 'index-name', 'objectID': 'foo_id', 'attributesToRetrieve': ['firstname', 'lastname'] }] self.index.get_objects(['foo_id'], request_options) self.transporter.read.assert_called_once_with( 'POST', '1/indexes/*/objects', {'requests': requests}, # asserts version 2 it's used. request_options ) self.assertNotIn('attributesToRetrieve', request_options.data) def test_get_objects_with_attributes_to_retreive_bulk(self): request_options = RequestOptions.create(self.config, { 'attributesToRetrieve': ['firstname', 'lastname'] }) requests = [{ 'indexName': 'index-name', 'objectID': 'foo_id', 'attributesToRetrieve': ['firstname', 'lastname'] }, { 'indexName': 'index-name', 'objectID': 'bar_id', 'attributesToRetrieve': ['firstname', 'lastname'] }] self.index.get_objects(['foo_id', 'bar_id'], request_options) self.transporter.read.assert_called_once_with( 'POST', '1/indexes/*/objects', {'requests': requests}, # asserts version 2 it's used. request_options ) self.assertNotIn('attributesToRetrieve', request_options.data) def test_get_settings(self): self.transporter.read.return_value = { 'attributesToIndex': ['attr1', 'attr2'], 'numericAttributesToIndex': ['attr1', 'attr2'], 'slaves': ['index1', 'index2'], 'ignorePlurals': True, } settings = self.index.get_settings({'foo': 'bar'}) self.transporter.read.assert_called_once_with( 'GET', '1/indexes/index-name/settings', {'getVersion': 2}, # asserts version 2 it's used. {'foo': 'bar'} ) self.assertEqual(settings, { 'searchableAttributes': ['attr1', 'attr2'], 'numericAttributesForFiltering': ['attr1', 'attr2'], 'replicas': ['index1', 'index2'], 'ignorePlurals': True, }) def test_save_synonyms(self): # Test null response self.index.save_synonyms([]).wait() # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_synonyms([F.synonym(object_id=False)]) # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_synonym(F.synonym(object_id=False)) def test_save_rules(self): # Test null response self.index.save_rules([]).wait() # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_rule({'foo': 'bar'}) # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_rules([{'foo': 'bar'}]) def test_replace_all_objects(self): self.index._create_temporary_name = mock.Mock( name="_create_temporary_name") tmp_index_name = 'index-name_tmp_bar' self.index._create_temporary_name.return_value = tmp_index_name # noqa: E501 obj = F.obj() self.index.replace_all_objects([obj]) # Asserts the operations of the replace all objects. self.transporter.write.assert_has_calls( [mock.call('POST', '1/indexes/index-name/operation', {'operation': 'copy', 'destination': 'index-name_tmp_bar'}, {'scope': ['settings', 'synonyms', 'rules']}), mock.call('POST', '1/indexes/index-name_tmp_bar/batch', {'requests': [ {'action': 'updateObject', 'body': obj}]}, None), mock.call('POST', '1/indexes/index-name_tmp_bar/operation', {'operation': 'move', 'destination': 'index-name'}, None)] ) response = NullResponse() response.wait = mock.Mock(name="wait") self.index.copy_to = mock.Mock( name="copy_to") self.index.copy_to.return_value = response self.index.move_to = mock.Mock( name="move_to") self.index.move_to.return_value = response self.index.save_objects = mock.Mock( name="save_objects") self.index.save_objects.return_value = response self.index.replace_all_objects([obj]) self.assertEqual(response.wait.call_count, 0) result = self.index.replace_all_objects([obj], {'safe': True}) self.assertEqual(response.wait.call_count, 3) self.assertEqual(len(result.responses), 3) self.assertEqual(len(result._waitable), 0) def test_get_task(self): with self.assertRaises(AssertionError) as _: self.index.get_task('')
class TestSearchIndex(unittest.TestCase): def setUp(self): self.config = SearchConfig('foo', 'bar') requester = Requester() self.transporter = Transporter(requester, self.config) self.transporter.read = mock.Mock(name="read") self.transporter.read.return_value = {} self.transporter.write = mock.Mock(name="write") self.transporter.write.return_value = {} self.index = SearchIndex(self.transporter, self.config, 'index-name') def test_app_id_getter(self): self.assertEqual(self.index.app_id, 'foo') def test_name_getter(self): self.assertEqual(self.index.name, 'index-name') def test_save_objects(self): # Saving an object without object id with self.assertRaises(MissingObjectIdException) as _: self.index.save_objects([{'foo': 'bar'}]) with self.assertRaises(MissingObjectIdException) as _: self.index.save_objects([{'foo': 'bar'}], {'autoGenerateObjectIDIfNotExist': False}) self.index.save_objects([{'foo': 'bar'}], {'autoGenerateObjectIDIfNotExist': True}) self.transporter.write.assert_called_once_with( 'POST', '1/indexes/index-name/batch', {'requests': [{'action': 'addObject', 'body': {'foo': 'bar'}}]}, {}, ) self.transporter.write = mock.Mock(name="write") self.index.save_objects([{'foo': 'bar', 'objectID': 'foo'}]) self.transporter.write.assert_called_once_with( 'POST', '1/indexes/index-name/batch', { 'requests': [ { 'action': 'updateObject', 'body': {'foo': 'bar', 'objectID': 'foo'}} ] }, None ) def test_partial_update_objects(self): # Saving an object without object id with self.assertRaises(MissingObjectIdException) as _: self.index.partial_update_objects([{'foo': 'bar'}]) with self.assertRaises(MissingObjectIdException) as _: self.index.partial_update_objects([{'foo': 'bar'}], {'createIfNotExists': False}) self.index.partial_update_objects([{'foo': 'bar'}], {'createIfNotExists': True}) self.transporter.write.assert_called_once_with( 'POST', '1/indexes/index-name/batch', {'requests': [ {'action': 'partialUpdateObject', 'body': {'foo': 'bar'}}]}, {}, ) self.transporter.write = mock.Mock(name="write") self.index.partial_update_objects([{'foo': 'bar', 'objectID': 'foo'}]) self.transporter.write.assert_called_once_with( 'POST', '1/indexes/index-name/batch', { 'requests': [ { 'action': 'partialUpdateObjectNoCreate', 'body': {'foo': 'bar', 'objectID': 'foo'} } ] }, None, ) def test_get_settings(self): self.transporter.read.return_value = { 'attributesToIndex': ['attr1', 'attr2'], 'numericAttributesToIndex': ['attr1', 'attr2'], 'slaves': ['index1', 'index2'], 'ignorePlurals': True, } settings = self.index.get_settings({'foo': 'bar'}) self.transporter.read.assert_called_once_with( 'GET', '1/indexes/index-name/settings', {'getVersion': 2}, # asserts version 2 it's used. {'foo': 'bar'} ) self.assertEqual(settings, { 'searchableAttributes': ['attr1', 'attr2'], 'numericAttributesForFiltering': ['attr1', 'attr2'], 'replicas': ['index1', 'index2'], 'ignorePlurals': True, }) def test_save_synonyms(self): # Test null response self.index.save_synonyms([]).wait() # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_synonyms([F.synonym(object_id=False)]) # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_synonym(F.synonym(object_id=False)) def test_save_rules(self): # Test null response self.index.save_rules([]).wait() # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_rule({'foo': 'bar'}) # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_rules([{'foo': 'bar'}]) def test_replace_all_objects(self): self.index._create_temporary_name = mock.Mock( name="_create_temporary_name") tmp_index_name = 'index-name_tmp_bar' self.index._create_temporary_name.return_value = tmp_index_name # noqa: E501 obj = F.obj() self.index.replace_all_objects([obj]) # Asserts the operations of the replace all objects. self.transporter.write.assert_has_calls( [mock.call('POST', '1/indexes/index-name/operation', {'operation': 'copy', 'destination': 'index-name_tmp_bar'}, {'scope': ['settings', 'synonyms', 'rules']}), mock.call('POST', '1/indexes/index-name_tmp_bar/batch', {'requests': [ {'action': 'updateObject', 'body': obj}]}, None), mock.call('POST', '1/indexes/index-name_tmp_bar/operation', {'operation': 'move', 'destination': 'index-name'}, None)] ) response = NullResponse() response.wait = mock.Mock(name="wait") self.index.copy_to = mock.Mock( name="copy_to") self.index.copy_to.return_value = response self.index.move_to = mock.Mock( name="move_to") self.index.move_to.return_value = response self.index.save_objects = mock.Mock( name="save_objects") self.index.save_objects.return_value = response self.index.replace_all_objects([obj]) self.assertEqual(response.wait.call_count, 0) result = self.index.replace_all_objects([obj], {'safe': True}) self.assertEqual(response.wait.call_count, 3) self.assertEqual(len(result.responses), 3) self.assertEqual(len(result._waitable), 0) def test_get_task(self): with self.assertRaises(AssertionError) as _: self.index.get_task('')
class TestSearchIndex(unittest.TestCase): def setUp(self): self.config = SearchConfig('foo', 'bar') requester = Requester() self.transporter = Transporter(requester, self.config) self.transporter.read = mock.Mock(name="read") self.transporter.read.return_value = {} self.transporter.write = mock.Mock(name="write") self.transporter.write.return_value = {} self.index = SearchIndex(self.transporter, self.config, 'index-name') def test_app_id_getter(self): self.assertEqual(self.index.app_id, 'foo') def test_name_getter(self): self.assertEqual(self.index.name, 'index-name') def test_exists(self): with mock.patch.object(self.index, 'get_settings') as submethod_mock: submethod_mock.side_effect = RequestException( "Index does not exist", 404) indexExists = self.index.exists() self.index.get_settings.assert_called_once() self.assertEqual(indexExists, False) # No request options args = self.index.get_settings.call_args[0] self.assertEqual(args[0], None) self.assertEqual(indexExists, False) with mock.patch.object(self.index, 'get_settings') as submethod_mock: submethod_mock.side_effect = RequestException( "Permissions error", 400) with self.assertRaises(RequestException) as _: self.index.exists() with mock.patch.object(self.index, 'get_settings') as submethod_mock: submethod_mock.return_value = { 'hitsPerPage': 20, 'maxValuesPerFacet': 100 } request_options = {'X-Algolia-User-ID': 'Foo'} indexExists = self.index.exists(request_options) # With request options args = self.index.get_settings.call_args[0] self.assertEqual(args[0], request_options) self.index.get_settings.assert_called_once() self.assertEqual(indexExists, True) with mock.patch.object(self.index, 'get_settings') as submethod_mock: submethod_mock.return_value = { 'hitsPerPage': 20, 'maxValuesPerFacet': 100 } indexExists = self.index.exists() self.index.get_settings.assert_called_once() self.assertEqual(indexExists, True) def test_save_objects(self): # Saving an object without object id with self.assertRaises(MissingObjectIdException) as _: self.index.save_objects([{'foo': 'bar'}]) with self.assertRaises(MissingObjectIdException) as _: self.index.save_objects([{ 'foo': 'bar' }], {'autoGenerateObjectIDIfNotExist': False}) self.index.save_objects([{ 'foo': 'bar' }], {'autoGenerateObjectIDIfNotExist': True}) self.transporter.write.assert_called_once_with( 'POST', '1/indexes/index-name/batch', {'requests': [{ 'action': 'addObject', 'body': { 'foo': 'bar' } }]}, {}, ) self.transporter.write = mock.Mock(name="write") self.index.save_objects([{'foo': 'bar', 'objectID': 'foo'}]) self.transporter.write.assert_called_once_with( 'POST', '1/indexes/index-name/batch', { 'requests': [{ 'action': 'updateObject', 'body': { 'foo': 'bar', 'objectID': 'foo' } }] }, None) def test_partial_update_objects(self): # Saving an object without object id with self.assertRaises(MissingObjectIdException) as _: self.index.partial_update_objects([{'foo': 'bar'}]) with self.assertRaises(MissingObjectIdException) as _: self.index.partial_update_objects([{ 'foo': 'bar' }], {'createIfNotExists': False}) self.index.partial_update_objects([{ 'foo': 'bar' }], {'createIfNotExists': True}) self.transporter.write.assert_called_once_with( 'POST', '1/indexes/index-name/batch', { 'requests': [{ 'action': 'partialUpdateObject', 'body': { 'foo': 'bar' } }] }, {}, ) self.transporter.write = mock.Mock(name="write") self.index.partial_update_objects([{'foo': 'bar', 'objectID': 'foo'}]) self.transporter.write.assert_called_once_with( 'POST', '1/indexes/index-name/batch', { 'requests': [{ 'action': 'partialUpdateObjectNoCreate', 'body': { 'foo': 'bar', 'objectID': 'foo' } }] }, None, ) def test_get_objects(self): request_options = RequestOptions.create(self.config) requests = [{'indexName': 'index-name', 'objectID': 'foo_id'}] self.index.get_objects(['foo_id'], request_options) self.transporter.read.assert_called_once_with( 'POST', '1/indexes/*/objects', {'requests': requests}, # asserts version 2 it's used. request_options) def test_get_objects_with_attributes_to_retreive(self): request_options = RequestOptions.create( self.config, {'attributesToRetrieve': ['firstname', 'lastname']}) requests = [{ 'indexName': 'index-name', 'objectID': 'foo_id', 'attributesToRetrieve': ['firstname', 'lastname'] }] self.index.get_objects(['foo_id'], request_options) self.transporter.read.assert_called_once_with( 'POST', '1/indexes/*/objects', {'requests': requests}, # asserts version 2 it's used. request_options) self.assertNotIn('attributesToRetrieve', request_options.data) def test_get_objects_with_attributes_to_retreive_bulk(self): request_options = RequestOptions.create( self.config, {'attributesToRetrieve': ['firstname', 'lastname']}) requests = [{ 'indexName': 'index-name', 'objectID': 'foo_id', 'attributesToRetrieve': ['firstname', 'lastname'] }, { 'indexName': 'index-name', 'objectID': 'bar_id', 'attributesToRetrieve': ['firstname', 'lastname'] }] self.index.get_objects(['foo_id', 'bar_id'], request_options) self.transporter.read.assert_called_once_with( 'POST', '1/indexes/*/objects', {'requests': requests}, # asserts version 2 it's used. request_options) self.assertNotIn('attributesToRetrieve', request_options.data) def test_get_settings(self): self.transporter.read.return_value = { 'attributesToIndex': ['attr1', 'attr2'], 'numericAttributesToIndex': ['attr1', 'attr2'], 'slaves': ['index1', 'index2'], 'ignorePlurals': True, } request_options = RequestOptions.create(self.config, {'foo': 'bar'}) settings = self.index.get_settings(request_options) self.transporter.read.assert_called_once_with( 'GET', '1/indexes/index-name/settings', None, request_options) self.assertEqual(request_options.query_parameters['getVersion'], 2) self.assertEqual( settings, { 'searchableAttributes': ['attr1', 'attr2'], 'numericAttributesForFiltering': ['attr1', 'attr2'], 'replicas': ['index1', 'index2'], 'ignorePlurals': True, }) def test_get_settings_none_as_request_options(self): self.index.get_settings() args = self.transporter.read.call_args[0] self.assertEqual(args[3].query_parameters['getVersion'], 2) def test_get_settings_dict_as_request_options(self): self.index.get_settings({'foo': 'bar'}) args = self.transporter.read.call_args[0] self.assertEqual(args[3].query_parameters['getVersion'], 2) def test_get_settings_with_request_options(self): request_options = RequestOptions.create(self.config, {'foo': 'bar'}) self.index.get_settings(request_options) args = self.transporter.read.call_args[0] self.assertEqual(args[3].query_parameters['getVersion'], 2) def test_save_synonyms(self): # Test null response self.index.save_synonyms([]).wait() # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_synonyms([F.synonym(object_id=False)]) # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_synonym(F.synonym(object_id=False)) def test_save_rules(self): # Test null response self.index.save_rules([]).wait() # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_rule({'foo': 'bar'}) # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_rules([{'foo': 'bar'}]) def test_find_object(self): self.index.search = mock.Mock(name="search") self.index.search.return_value = { 'hits': [{ 'foo': 'bar' }], 'nbPages': 1 } self.index.find_object(lambda obj: True) args, _ = self.index.search.call_args self.assertEqual(args[0], '') self.assertEqual(args[1].data, RequestOptions.create(self.config, { 'page': 0 }).data) self.index.find_object(lambda obj: True, { 'query': 'foo', 'hitsPerPage': 5 }) args, _ = self.index.search.call_args self.assertEqual(args[0], 'foo') self.assertEqual( args[1].data, RequestOptions.create(self.config, { 'hitsPerPage': 5, 'page': 0 }).data) self.index.find_object( lambda obj: True, RequestOptions.create(self.config, {'User-Agent': 'blabla'})) args, _ = self.index.search.call_args self.assertEqual(args[0], '') self.assertEqual(args[1].data, RequestOptions.create(self.config, { 'page': 0 }).data) self.assertEqual( args[1].headers, RequestOptions.create(self.config, { 'User-Agent': 'blabla' }).headers) def test_replace_all_objects(self): self.index._create_temporary_name = mock.Mock( name="_create_temporary_name") tmp_index_name = 'index-name_tmp_bar' self.index._create_temporary_name.return_value = tmp_index_name # noqa: E501 obj = F.obj() self.index.replace_all_objects([obj]) # Asserts the operations of the replace all objects. self.transporter.write.assert_has_calls([ mock.call('POST', '1/indexes/index-name/operation', { 'operation': 'copy', 'destination': 'index-name_tmp_bar' }, {'scope': ['settings', 'synonyms', 'rules']}), mock.call('POST', '1/indexes/index-name_tmp_bar/batch', {'requests': [{ 'action': 'updateObject', 'body': obj }]}, None), mock.call('POST', '1/indexes/index-name_tmp_bar/operation', { 'operation': 'move', 'destination': 'index-name' }, None) ]) response = NullResponse() response.wait = mock.Mock(name="wait") self.index.copy_to = mock.Mock(name="copy_to") self.index.copy_to.return_value = response self.index.move_to = mock.Mock(name="move_to") self.index.move_to.return_value = response self.index.save_objects = mock.Mock(name="save_objects") self.index.save_objects.return_value = response self.index.replace_all_objects([obj]) self.assertEqual(response.wait.call_count, 0) result = self.index.replace_all_objects([obj], {'safe': True}) self.assertEqual(response.wait.call_count, 3) self.assertEqual(len(result.responses), 3) self.assertEqual(len(result._waitable), 0) def test_get_task(self): with self.assertRaises(AssertionError) as _: self.index.get_task('')
class TestSearchIndex(unittest.TestCase): def setUp(self): self.config = SearchConfig("foo", "bar") requester = Requester() self.transporter = Transporter(requester, self.config) self.transporter.read = mock.Mock(name="read") self.transporter.read.return_value = {} self.transporter.write = mock.Mock(name="write") self.transporter.write.return_value = {} self.index = SearchIndex(self.transporter, self.config, "index-name") def test_app_id_getter(self): self.assertEqual(self.index.app_id, "foo") def test_name_getter(self): self.assertEqual(self.index.name, "index-name") def test_exists(self): with mock.patch.object(self.index, "get_settings") as submethod_mock: submethod_mock.side_effect = RequestException( "Index does not exist", 404) indexExists = self.index.exists() self.index.get_settings.assert_called_once() self.assertEqual(indexExists, False) # No request options args = self.index.get_settings.call_args[0] self.assertEqual(args[0], None) self.assertEqual(indexExists, False) with mock.patch.object(self.index, "get_settings") as submethod_mock: submethod_mock.side_effect = RequestException( "Permissions error", 400) with self.assertRaises(RequestException) as _: self.index.exists() with mock.patch.object(self.index, "get_settings") as submethod_mock: submethod_mock.return_value = { "hitsPerPage": 20, "maxValuesPerFacet": 100 } request_options = {"X-Algolia-User-ID": "Foo"} indexExists = self.index.exists(request_options) # With request options args = self.index.get_settings.call_args[0] self.assertEqual(args[0], request_options) self.index.get_settings.assert_called_once() self.assertEqual(indexExists, True) with mock.patch.object(self.index, "get_settings") as submethod_mock: submethod_mock.return_value = { "hitsPerPage": 20, "maxValuesPerFacet": 100 } indexExists = self.index.exists() self.index.get_settings.assert_called_once() self.assertEqual(indexExists, True) def test_save_objects(self): # Saving an object without object id with self.assertRaises(MissingObjectIdException) as _: self.index.save_objects([{"foo": "bar"}]) with self.assertRaises(MissingObjectIdException) as _: self.index.save_objects([{ "foo": "bar" }], {"autoGenerateObjectIDIfNotExist": False}) self.index.save_objects([{ "foo": "bar" }], {"autoGenerateObjectIDIfNotExist": True}) self.transporter.write.assert_called_once_with( "POST", "1/indexes/index-name/batch", {"requests": [{ "action": "addObject", "body": { "foo": "bar" } }]}, {}, ) self.transporter.write = mock.Mock(name="write") self.index.save_objects([{"foo": "bar", "objectID": "foo"}]) self.transporter.write.assert_called_once_with( "POST", "1/indexes/index-name/batch", { "requests": [{ "action": "updateObject", "body": { "foo": "bar", "objectID": "foo" }, }] }, None, ) def test_partial_update_objects(self): # Saving an object without object id with self.assertRaises(MissingObjectIdException) as _: self.index.partial_update_objects([{"foo": "bar"}]) with self.assertRaises(MissingObjectIdException) as _: self.index.partial_update_objects([{ "foo": "bar" }], {"createIfNotExists": False}) self.index.partial_update_objects([{ "foo": "bar" }], {"createIfNotExists": True}) self.transporter.write.assert_called_once_with( "POST", "1/indexes/index-name/batch", { "requests": [{ "action": "partialUpdateObject", "body": { "foo": "bar" } }] }, {}, ) self.transporter.write = mock.Mock(name="write") self.index.partial_update_objects([{"foo": "bar", "objectID": "foo"}]) self.transporter.write.assert_called_once_with( "POST", "1/indexes/index-name/batch", { "requests": [{ "action": "partialUpdateObjectNoCreate", "body": { "foo": "bar", "objectID": "foo" }, }] }, None, ) def test_get_objects(self): request_options = RequestOptions.create(self.config) requests = [{"indexName": "index-name", "objectID": "foo_id"}] self.index.get_objects(["foo_id"], request_options) self.transporter.read.assert_called_once_with( "POST", "1/indexes/*/objects", {"requests": requests}, # asserts version 2 it's used. request_options, ) def test_get_objects_with_attributes_to_retreive(self): request_options = RequestOptions.create( self.config, {"attributesToRetrieve": ["firstname", "lastname"]}) requests = [{ "indexName": "index-name", "objectID": "foo_id", "attributesToRetrieve": ["firstname", "lastname"], }] self.index.get_objects(["foo_id"], request_options) self.transporter.read.assert_called_once_with( "POST", "1/indexes/*/objects", {"requests": requests}, # asserts version 2 it's used. request_options, ) self.assertNotIn("attributesToRetrieve", request_options.data) def test_get_objects_with_attributes_to_retreive_bulk(self): request_options = RequestOptions.create( self.config, {"attributesToRetrieve": ["firstname", "lastname"]}) requests = [ { "indexName": "index-name", "objectID": "foo_id", "attributesToRetrieve": ["firstname", "lastname"], }, { "indexName": "index-name", "objectID": "bar_id", "attributesToRetrieve": ["firstname", "lastname"], }, ] self.index.get_objects(["foo_id", "bar_id"], request_options) self.transporter.read.assert_called_once_with( "POST", "1/indexes/*/objects", {"requests": requests}, # asserts version 2 it's used. request_options, ) self.assertNotIn("attributesToRetrieve", request_options.data) def test_get_settings(self): self.transporter.read.return_value = { "attributesToIndex": ["attr1", "attr2"], "numericAttributesToIndex": ["attr1", "attr2"], "slaves": ["index1", "index2"], "ignorePlurals": True, } request_options = RequestOptions.create(self.config, {"foo": "bar"}) settings = self.index.get_settings(request_options) self.transporter.read.assert_called_once_with( "GET", "1/indexes/index-name/settings", None, request_options) self.assertEqual(request_options.query_parameters["getVersion"], 2) self.assertEqual( settings, { "searchableAttributes": ["attr1", "attr2"], "numericAttributesForFiltering": ["attr1", "attr2"], "replicas": ["index1", "index2"], "ignorePlurals": True, }, ) def test_get_settings_none_as_request_options(self): self.index.get_settings() args = self.transporter.read.call_args[0] self.assertEqual(args[3].query_parameters["getVersion"], 2) def test_get_settings_dict_as_request_options(self): self.index.get_settings({"foo": "bar"}) args = self.transporter.read.call_args[0] self.assertEqual(args[3].query_parameters["getVersion"], 2) def test_get_settings_with_request_options(self): request_options = RequestOptions.create(self.config, {"foo": "bar"}) self.index.get_settings(request_options) args = self.transporter.read.call_args[0] self.assertEqual(args[3].query_parameters["getVersion"], 2) def test_save_synonyms(self): # Test null response self.index.save_synonyms([]).wait() # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_synonyms([F.synonym(object_id=False)]) # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_synonym(F.synonym(object_id=False)) def test_save_rules(self): # Test null response self.index.save_rules([]).wait() # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_rule({"foo": "bar"}) # Test object id validation with self.assertRaises(MissingObjectIdException) as _: self.index.save_rules([{"foo": "bar"}]) def test_find_object(self): self.index.search = mock.Mock(name="search") self.index.search.return_value = { "hits": [{ "foo": "bar" }], "nbPages": 1 } self.index.find_object(lambda obj: True) args, _ = self.index.search.call_args self.assertEqual(args[0], "") self.assertEqual(args[1].data, RequestOptions.create(self.config, { "page": 0 }).data) self.index.find_object(lambda obj: True, { "query": "foo", "hitsPerPage": 5 }) args, _ = self.index.search.call_args self.assertEqual(args[0], "foo") self.assertEqual( args[1].data, RequestOptions.create(self.config, { "hitsPerPage": 5, "page": 0 }).data, ) self.index.find_object( lambda obj: True, RequestOptions.create(self.config, {"User-Agent": "blabla"}), ) args, _ = self.index.search.call_args self.assertEqual(args[0], "") self.assertEqual(args[1].data, RequestOptions.create(self.config, { "page": 0 }).data) self.assertEqual( args[1].headers, RequestOptions.create(self.config, { "User-Agent": "blabla" }).headers, ) def test_replace_all_objects(self): self.index._create_temporary_name = mock.Mock( name="_create_temporary_name") tmp_index_name = "index-name_tmp_bar" self.index._create_temporary_name.return_value = tmp_index_name # noqa: E501 obj = F.obj() self.index.replace_all_objects([obj]) # Asserts the operations of the replace all objects. self.transporter.write.assert_has_calls([ mock.call( "POST", "1/indexes/index-name/operation", { "operation": "copy", "destination": "index-name_tmp_bar" }, {"scope": ["settings", "synonyms", "rules"]}, ), mock.call( "POST", "1/indexes/index-name_tmp_bar/batch", {"requests": [{ "action": "updateObject", "body": obj }]}, None, ), mock.call( "POST", "1/indexes/index-name_tmp_bar/operation", { "operation": "move", "destination": "index-name" }, None, ), ]) self.index._transporter.read = mock.Mock(name="read") self.index._transporter.read.return_value = {"status": "published"} self.index._transporter.write = mock.Mock(name="write") self.index._transporter.write.return_value = {"taskID": 1} self.index.replace_all_objects([obj]) self.assertEqual(self.index._transporter.write.call_count, 3) self.index.replace_all_objects([obj], {"safe": True}) self.assertEqual(self.index._transporter.write.call_count, 6) # 3+3 self.assertEqual(self.index._transporter.read.call_count, 3) # 3 waits def test_get_task(self): with self.assertRaises(AssertionError) as _: self.index.get_task("")
def test_search(self): responses = MultipleResponse() responses.push( self.index.save_objects( [ { "company": "Algolia", "name": "Julien Lemoine", "objectID": "julien-lemoine", }, # noqa: E501 { "company": "Algolia", "name": "Nicolas Dessaigne", "objectID": "nicolas-dessaigne", }, # noqa: E501 { "company": "Amazon", "name": "Jeff Bezos" }, { "company": "Apple", "name": "Steve Jobs" }, { "company": "Apple", "name": "Steve Wozniak" }, { "company": "Arista Networks", "name": "Jayshree Ullal" }, { "company": "Google", "name": "Larry Page" }, { "company": "Google", "name": "Rob Pike" }, { "company": "Google", "name": "Serguey Brin" }, { "company": "Microsoft", "name": "Bill Gates" }, { "company": "SpaceX", "name": "Elon Musk" }, { "company": "Tesla", "name": "Elon Musk" }, { "company": "Yahoo", "name": "Marissa Mayer" }, ], {"autoGenerateObjectIDIfNotExist": True}, )) responses.push( self.index.set_settings( {"attributesForFaceting": ["searchable(company)"]})) responses.wait() # Perform a search query using search with the query `algolia` and no # parameter and check that the number of returned hits is equal to 2 result = self.index.search("algolia") self.assertEqual(result["nbHits"], 2) self.assertEqual( SearchIndex.get_object_position(result, "nicolas-dessaigne"), 0) self.assertEqual( SearchIndex.get_object_position(result, "julien-lemoine"), 1) self.assertEqual(SearchIndex.get_object_position(result, ""), -1) # Call find_object with the following parameters and check that # no object is found with self.assertRaises(ObjectNotFoundException): self.index.find_object(lambda _: False) # Call find_object with the following parameters and check that # the first object is returned with a `position=0` and `page=0` found = self.index.find_object(lambda _: True) self.assertEqual(found["position"], 0) self.assertEqual(found["page"], 0) def callback(obj): # type: (dict) -> bool return obj.get("company") == "Apple" # Call find_object with the following parameters and check that # no object is found with self.assertRaises(ObjectNotFoundException): self.index.find_object(callback, {"query": "algolia"}) # Call find_object with the following parameters and check that # no object is found with self.assertRaises(ObjectNotFoundException): self.index.find_object(callback, { "query": "", "paginate": False, "hitsPerPage": 5 }) # Call find_object with the following parameters and check that # the first object is returned with a `position=0` and `page=2` found = self.index.find_object(callback, { "query": "", "paginate": True, "hitsPerPage": 5 }) self.assertEqual(found["position"], 0) self.assertEqual(found["page"], 2) # Perform a search using search with the query `elon` and the # following parameter and check that the queryID field from # the response is not empty result = self.index.search("elon", {"clickAnalytics": True}) self.assertIn("queryID", result) # Perform a faceted search using search with the query `elon` and the # following parameters and check that the number of returned hits is # equal to 1 result = self.index.search("elon", { "facets": "*", "facetFilters": "company:tesla" }) self.assertEqual(result["nbHits"], 1) # Perform a filtered search using search with the query `elon` and the # following parameters and check that the number of returned hits is # equal to 2 result = self.index.search( "elon", { "facets": "*", "filters": "(company:tesla OR company:spacex)" }) self.assertEqual(result["nbHits"], 2) result = self.index.search_for_facet_values("company", "a")["facetHits"] values = list(map(lambda facet: facet["value"], result)) self.assertIn("Algolia", values) self.assertIn("Amazon", values) self.assertIn("Apple", values) self.assertIn("Arista Networks", values)