Data Import Handler – usuwanie danych z indeksu

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.

This post is also available in: angielski

This entry was posted on poniedziałek, Styczeń 3rd, 2011 at 09:06 and is filed under Bez kategorii, Solr. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

One Response to “Data Import Handler – usuwanie danych z indeksu”

  1. Joanna Says:

    cool 🙂 dokladnie wlasnie tak sobie to wyobrazalam 😛