Esempio n. 1
0
 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)
Esempio n. 4
0
    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"],
        )
Esempio n. 5
0
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')
Esempio n. 7
0
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
Esempio n. 8
0
    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('')
Esempio n. 11
0
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('')
Esempio n. 12
0
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('')
Esempio n. 13
0
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("")
Esempio n. 14
0
    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)