Usuwanie danych z indeksu przy wykorzystaniu indeksowania przyrostowego w DIH jest na wiki SOLR potraktowane szczątkowo, jako coś, co działa analogicznie do aktualizacji rekordów. Podobnie we wcześniejszym artykule użyłem tego skrótu, tym bardziej, że podany przeze mnie przykład z indeksowaniem zasobów wikipedii nie potrzebował usuwania danych.
Mając pod ręką przykładowe dane z albumami i wykonawcami postanowiłem pokazać mój sposób postępowania w takich wypadkach. Dla uproszczenia i przejrzystości zakładam, że po pierwszym zaimportowaniu, danych może tylko ubywać.
Dane testowe
Moje dane testowe mieszczą się bazie PostgreSQL w tabeli zdefiniowanej następująco:
Table "public.albums" Column | Type | Modifiers --------+---------+----------------------------------------------------- id | integer | not null default nextval('albums_id_seq'::regclass) name | text | not null author | text | not null Indexes: "albums_pk" PRIMARY KEY, btree (id)
W tabeli znajduje się 825661 rekordów.
Instalacja testowa
Do testów użyłem instancji SOLR posiadającej następującą charakterystykę:
Definicja w schema.xml:
<fields> <field name="id" type="string" indexed="true" stored="true" required="true" /> <field name="album" type="text" indexed="true" stored="true" multiValued="true"/> <field name="author" type="text" indexed="true" stored="true" multiValued="true"/> </fields> <uniqueKey>id</uniqueKey> <defaultSearchField>album</defaultSearchField>
Definicja DIH w solrconfig.xml:
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler"> <lst name="defaults"> <str name="config">db-data-config.xml</str> </lst> </requestHandler>
I plik DIH db-data-config.xml:
<dataConfig> <dataSource driver="org.postgresql.Driver" url="jdbc:postgresql://localhost:5432/shardtest" user="solr" password="secret" /> <document> <entity name="album" query="SELECT * from albums"> <field column="id" name="id" /> <field column="name" name="album" /> <field column="author" name="author" /> </entity> </document> </dataConfig>
Przed naszym testem zaimportowałem wszystkie dane z tabeli albums.
Usuwanie danych
Patrząc na tabelę widać, że gdy usuniemy rekord, ginie on bez śladu i jedynym sposobem aktualizacji naszego indeksu byłoby porównanie identyfikatorów dokumentów w indeksie z identyfikatorami w bazie i wyrzucenie tych, które w bazie już nie istnieją. Wolne i niewygodne. Innym sposobem jest dodatnie kolumny deleted_at: zamiast kasowania fizycznie rekordu, uzupełniamy tylko tę kolumnę. DIH może wtedy pobrać wszystkie rekordy z ustawioną datą późniejszą od ostatniego indeksowania. Wadą tego rozwiązania może by konieczność modyfikacji aplikacji by uwzględniały tak „skasowane” rekordy.
Ja zastosuje inne rozwiązanie, przeźroczyste dla aplikacji. Tworzymy nową tabelę:
CREATE TABLE deletes ( id serial NOT NULL, deleted_id bigint, deleted_at timestamp without time zone NOT NULL, CONSTRAINT deletes_pk PRIMARY KEY (id) );
Do tej tabeli automagicznie będziemy dopisywać identyfikatory tych elementów, które zostały usunięte z tabeli albums oraz informacje kiedy zostały usunięte.
Teraz dodamy jeszcze funkcję:
CREATE OR REPLACE FUNCTION insert_after_delete() RETURNS trigger AS $BODY$BEGIN IF tg_op = 'DELETE' THEN INSERT INTO deletes(deleted_id, deleted_at) VALUES (old.id, now()); RETURN old; END IF; END$BODY$ LANGUAGE plpgsql VOLATILE;
oraz trigger:
CREATE TRIGGER deleted_trg BEFORE DELETE ON albums FOR EACH ROW EXECUTE PROCEDURE insert_after_delete();
Sprawdzamy działanie
Zgodnie z planem, każdy usunięty wpis w tabeli albums powinien skutkować uzupełnieniem tabeli
deletes. Sprawdźmy więc. Usuwamy parę rekordów:
=> DELETE FROM albums where id < 37; DELETE 2 => SELECT * from deletes; id | deleted_id | deleted_at ----+------------+---------------------------- 26 | 35 | 2010-12-23 13:53:18.034612 27 | 36 | 2010-12-23 13:53:18.034612 (2 rows)
Czyli baza działa.
Uzupełniamy plik konfiguracyjny DIH tak, by entity było zdefiniowane następująco:
<entity name="album" query="SELECT * from albums" deletedPkQuery="SELECT deleted_id as id FROM deletes WHERE deleted_at > '$'{dataimporter.last_index_time}'">
Dzięki temu przy imporcie przyrostowym DIH użyje atrybutu deletedPkQuery by pobrać identyfikatory tych dokumentów, które należy usunąć.
Sprytny czytelnik pewnie zacznie się zastanawiać, czy na pewno potrzebna jest nam kolumna z datą usunięcia rekordu. Przecież możemy usunąć wszystkie rekordy znalezione w tabeli deleted a następnie skasować zawartość tej tabeli. Teoretycznie to prawda, ale w przypadku problemu z serwerem indeksującym SOLR w naszym wypadku łatwo zastąpić go innym – jego stopień synchronizacji z bazą nie jest bardzo istotny – po prostu za następnym importem przyrostowym nastąpi synchronizacja z bazą. W opcji z kasowaniem zawartości deletes takie możliwości nie ma.
Wykonujemy teraz import przyrostowy wywołując adres: /solr/dataimport?command=delta-import
W logach powinna pojawić się linia podobna do tej:
INFO: {delete=[35, 36],optimize=} 0 2
Co oznacza, że DIH poprawnie usunął z indeksu te dokumenty, które usunęliśmy wcześniej z bazy.