diff --git a/ftw/catalogdoctor/surgery.py b/ftw/catalogdoctor/surgery.py index 8a76b58..6d2c555 100644 --- a/ftw/catalogdoctor/surgery.py +++ b/ftw/catalogdoctor/surgery.py @@ -62,6 +62,19 @@ def perform(self): pass +class RemoveFromUUIDIndex(IndexSurgery): + """Remove rid from a `UUIDIndex`.""" + + def _remove_keys_pointing_to_rid(self, index): + for key in find_keys_pointing_to_rid(index, self.rid): + del index[key] + self._decrease_length() + + def perform(self): + self._remove_keys_pointing_to_rid(self.index._index) + self._remove_rid_from_unindex(self.index._unindex) + + class RemoveFromUnIndex(IndexSurgery): """Remove a rid from a simple forward and reverse index.""" @@ -126,6 +139,7 @@ class Surgery(object): FieldIndex: RemoveFromUnIndex, GopipIndex: NullSurgery, # not a real index KeywordIndex: RemoveFromUnIndex, + UUIDIndex: RemoveFromUUIDIndex, ZCTextIndex: UnindexObject, } @@ -144,7 +158,7 @@ def unindex_rid_from_all_catalog_indexes(self, rid): surgery(idx, rid).perform() continue - if not isinstance(idx, (ExtendedPathIndex, UUIDIndex)): + if not isinstance(idx, (ExtendedPathIndex,)): raise CantPerformSurgery( 'Unhandled index type: {0!r}'.format(idx)) diff --git a/ftw/catalogdoctor/tests/test_surgery_remove_from_uuidindex.py b/ftw/catalogdoctor/tests/test_surgery_remove_from_uuidindex.py new file mode 100644 index 0000000..cec449e --- /dev/null +++ b/ftw/catalogdoctor/tests/test_surgery_remove_from_uuidindex.py @@ -0,0 +1,68 @@ +from ftw.builder import Builder +from ftw.builder import create +from ftw.catalogdoctor.surgery import RemoveFromUUIDIndex +from ftw.catalogdoctor.tests import FunctionalTestCase +from ftw.catalogdoctor.utils import find_keys_pointing_to_rid + + +class TestRemoveFromUUIDIndex(FunctionalTestCase): + + def setUp(self): + super(TestRemoveFromUUIDIndex, self).setUp() + + self.grant('Contributor') + self.folder = create(Builder('folder').titled(u'Foo')) + + def test_remove_healthy_object(self): + rid = self.get_rid(self.folder) + index = self.catalog.indexes['UID'] + + entries_pointing_to_rid = find_keys_pointing_to_rid(index._index, rid) + self.assertEqual(1, len(entries_pointing_to_rid)) + self.assertIn(rid, index._unindex) + self.assertEqual(1, len(index)) + + surgery = RemoveFromUUIDIndex(index, rid) + surgery.perform() + + entries_pointing_to_rid = find_keys_pointing_to_rid(index._index, rid) + self.assertEqual(0, len(entries_pointing_to_rid)) + self.assertNotIn(rid, index._unindex) + self.assertEqual(0, len(index)) + + def test_remove_from_reverse_index_only(self): + rid = self.get_rid(self.folder) + index = self.catalog.indexes['UID'] + + entries_pointing_to_rid = find_keys_pointing_to_rid(index._index, rid) + del index._index[entries_pointing_to_rid[0]] + index._length.change(-1) + + self.assertIn(rid, index._unindex) + self.assertEqual(1, len(index._unindex)) + + surgery = RemoveFromUUIDIndex(index, rid) + surgery.perform() + + entries_pointing_to_rid = find_keys_pointing_to_rid(index._index, rid) + self.assertEqual(0, len(entries_pointing_to_rid)) + self.assertNotIn(rid, index._unindex) + self.assertEqual(0, len(index)) + + def test_remove_from_forward_index_only(self): + rid = self.get_rid(self.folder) + index = self.catalog.indexes['UID'] + + del index._unindex[rid] + + entries_pointing_to_rid = find_keys_pointing_to_rid(index._index, rid) + self.assertEqual(1, len(entries_pointing_to_rid)) + self.assertEqual(1, len(index._index)) + + surgery = RemoveFromUUIDIndex(index, rid) + surgery.perform() + + entries_pointing_to_rid = find_keys_pointing_to_rid(index._index, rid) + self.assertEqual(0, len(entries_pointing_to_rid)) + self.assertNotIn(rid, index._unindex) + self.assertEqual(0, len(index)) diff --git a/ftw/catalogdoctor/tests/test_utils.py b/ftw/catalogdoctor/tests/test_utils.py index f372079..8c4e01f 100644 --- a/ftw/catalogdoctor/tests/test_utils.py +++ b/ftw/catalogdoctor/tests/test_utils.py @@ -16,6 +16,13 @@ def test_find_keys_pointing_to_rid(self): self.assertItemsEqual( ['foo', 'bar'], find_keys_pointing_to_rid(dictish, 77)) + def test_find_keys_pointing_to_rid_single_value(self): + dictish = {'foo': 1, + 'bar': -23} + + self.assertItemsEqual( + ['bar'], find_keys_pointing_to_rid(dictish, -23)) + def test_find_keys_pointing_to_rid_emtpy_result(self): dictish = {'foo': [1, 77], 'qux': []} diff --git a/ftw/catalogdoctor/utils.py b/ftw/catalogdoctor/utils.py index 957ae8f..8170c78 100644 --- a/ftw/catalogdoctor/utils.py +++ b/ftw/catalogdoctor/utils.py @@ -1,7 +1,20 @@ def find_keys_pointing_to_rid(dictish, rid): - """Return all entries in dictish item pointing to rid.""" + """Return all entries in dictish item pointing to rid. + The values in dictish can be either the rid as a single value or a + collection containing the rid. + + """ return [ - key for key, rids_for_key in dictish.items() - if rid in rids_for_key + key for key, rids_or_rid in dictish.items() + if contains_or_equals_rid(rid, rids_or_rid) ] + + +def contains_or_equals_rid(rid, rids_or_rid): + """Return whether rids_or_rid contains or equals a rid.""" + + try: + return rid in rids_or_rid + except TypeError: + return rid == rids_or_rid