Suche verstehen
Wir helfen Ihnen dabei, die Such­anfragen Ihrer Kund*innen und Mit­ar­bei­­ter*innen zu ver­stehen und schnell re­le­vante Ergebnisse zu liefern.

Dokumente mit Solr updaten

In diesem Artikel wollen wir uns ansehen, wie das Aktualisieren einzelner Felder von Dokumenten in Solr funktioniert.
Bis Version 4 war es nicht möglich nur bestimmte Felder eines Dokuments zu aktualisieren, es musste immer das gesamte Dokumente an den Server geschickt werden.

Es gibt zwei Möglichkeiten Dokumente zu aktualisieren, welche sich nur in bestimmten Feldern geändert haben.

Das partielle Updaten von Dokumenten mittles atomarer Updates (atomic updates)

Mittels atomarer Updates können nun einzelne Felder eines Dokumentes verändert werden, ohne das gesamte Dokument nochmals an den Server senden zu müssen.
Somit muss nicht mehr das gesamte Dokument re-indexiert werden.

Damit dies korrekt funktioniert, müssen allerdings alle Felder außer der Zielfelder von copy fields, im Schema “stored” sein.

Field Modifiers für das Updaten von Feldern in Dokumenten

FeldmodifikatorAktionParameter
addHinzufügen eines oder mehrerer Werte zu einer ListeEinzelner Wert oder Liste von mehreren Werten
removeLöschen eines oder mehrerer Werte aus einer ListeEinzelner Wert oder Liste von mehreren Werten
incAddiert/Subtrahiert einen numerischen WerteEinzelner Wert
setSetzen oder Überschreiben des jeweiligen Feldwertes mit einem oder mehreren Werten.Einzelner Wert oder Liste von mehreren Werten.
Kann mit "null" oder einer leeren Liste auch entfernt werden
removeregexWerte, die auf einen regulären Ausdruck passen, aus einer Liste entfernenEinzelner Wert oder Liste von mehreren Werten

Beispiele für das partielle Aktualisieren

Dazu nehmen wir folgendes einfaches Schema für eine Leihvideothek.

 <?xml version="1.0" encoding="UTF-8" ?>
 <schema name="example_partial_update" version="1.5">
  <fieldType name="string" class="solr.StrField" sortMissingLast="true" />
  <fieldType name="int" class="solr.TrieIntField" precisionStep="0" positionIncrementGap="0" />
  <fieldType name="long" class="solr.TrieLongField" precisionStep="0" positionIncrementGap="0" />

  <fieldType name="text" class="solr.TextField" positionIncrementGap="100">
     <analyzer type="index">
       <tokenizer class="solr.StandardTokenizerFactory" />
       <filter class="solr.LowerCaseFilterFactory" />
     </analyzer>
     <analyzer type="query">
       <tokenizer class="solr.StandardTokenizerFactory" />
       <filter class="solr.LowerCaseFilterFactory" />
     </analyzer>
   </fieldType>

  <field name="_version_" type="long" indexed="true" stored="true" />
  <field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
  <field name="name" type="string" indexed="true" stored="true" required="true" multiValued="false" />
  <field name="description" type="text" indexed="true" stored="true" multiValued="false" />
  <field name="count" type="int" indexed="true" stored="true" required="true" multiValued="false" />
  <field name="genre" type="string" indexed="true" stored="true" required="true" multiValued="true" />

  <uniqueKey>id</uniqueKey>
 </schema>

Als erstes fügen wir unserer Videothek einen Film hinzu:

 curl -X POST --header "Content-Type: application/json" "http://localhost:8983/solr/videothek/update" -d '
 [ {"id" : "movie_1",
   "name" : "Roter Planet",
   "count" : 5,
   "genre" : "SciFi"
 }]'

Jetzt fügen wir eine Beschreibung hinzu, vermindern die Anzahl um zwei und fügen ein Genre hinzu.
Mittels eines commits können wir unsere Änderungen gleich sichtbar machen.

 curl -X POST --header "Content-Type: application/json" "http://localhost:8983/solr/videothek/update?commit=true" -d '
 [{"id" : "movie_1",
   "description" : { "set" : "Die erste bemannte Mission zum roten Planeten." },
   "count" : { "inc" : -2 },
   "genre" : { "add" : "Action" }
 }]'

Jetzt sollten wir die Änderungen sehen:

 curl "http://localhost:8983/solr/videothek/query?q=id:movie_1"
{
 "responseHeader":{"status":0,"QTime":25,"params":{"q":"id:movie_1"}},
 "response":{"numFound":1,"start":0,"docs":[
 {
   "id":"movie_1",
   "name":"Roter Planet",
   "count":3,
   "genre":["SciFi", "Action"],
   "description":"Die erste bemannte Mission zum roten Planeten.",
   "_version_":1506772835281928192
 }]}}

Wir entfernen “Action” wieder als Genre und fügen “Thriller” hinzu:

 curl "http://localhost:8983/solr/videothek/update" -d '
 [{"id" : "movie_1",
 "genre" : { "remove" : "Action", "add" : "Thriller" }
 }]'

Partielles Aktualisieren von Dokumenten mit SolrJ


package com.indoqa.demo;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.common.SolrInputDocument;

public class Demo {

    public void partialUpdate() throws SolrServerException, IOException {
        HttpSolrClient client = new HttpSolrClient("http://localhost:8983/solr/videothek");

        SolrInputDocument document = new SolrInputDocument();
        document.addField("id", "movie_1");

        Map<String, Object> fieldModifier = new HashMap<>();
        fieldModifier.put("add", "Thriller");
        fieldModifier.put("remove", "Action");

        document.addField("genre", fieldModifier);

        client.add(document);

        client.commit();
        client.shutdown();
    }
}
 

Dokumentenaktualisierung mittels optimistischer Nebenläufigkeit (optimistic concurrency) bzw. optimistischen Sperren (optimistic locking)

Mittels optimistischer Nebenläufigkeit ist es möglich, dass ein Dokument von mehreren Clients bearbeitet wird. Es werden allerdings nur die Änderungen desjenigen Clients übernommen, der als erster seine Änderungen zum Server geschickt hat. Alle anderen müssen das Dokument erneut laden.
Dies wird über das _version_ Feld, das Solr im ausgelieferten Schema definiert, für jedes Dokument gelöst.
Der Server antwortet mit einer HTTP 409 (version conflict) Response, wenn die übergebene Version nicht zu derjenigen im abgelegten Dokument passt.

Ein Client kann entweder das _version_ Feld eines Dokumentes befüllen oder, wenn nur ein Dokument aktualisiert werden soll, einen Requestparameter setzen.

Dabei kann das Verhalten des optimistischen Sperrens wie folgt über den Wert im Feld _version_ beeinflusst werden:

  • > 1 – Die Version des Dokumentes muss mit der Version im Index übereinstimmen
  • 1 – Das Dokument muss existieren, sonst wird das Update abgelehnt
  • 0 – Falls das Dokument im Index existiert, wird es überschrieben, ansonsten wird es hinzugefügt
  • < 0 – Das Dokument darf im Index nicht existieren, sonst wird das Update abgelehnt

Da bei einem Update das _version_ Feld garantiert einen neuen, höheren Wert als vor der Operation besitzt, kann man Solr mit dem Requestparameter versions=true dazu veranlassen, die neuen Versionen der Dokumente in der Antwort mitzuschicken, damit kein zusätzlicher Request abgeschickt werden muss.

Beispiele für Updates mit optimistischer Nebenläufigkeit in Solr

Update auf ein Dokument, das bereits existieren muss:

 curl -X POST --header "Content-Type: application/json" "http://localhost:8983/solr/videothek/update" -d '
 [{"id" : "movie_2",
   "name" : "Grüner Planet",
   "count" : 2,
   "genre" : "SciFi",
   "_version_": 1
 }]'
{"responseHeader":{"status":409,"QTime":2},
 "error":{"msg":"version conflict for movie_2 expected=1 actual=-1","code":409}
}

Dieses Update wurde abgelehnt, da das Dokument noch nicht in unserem Index existiert.

 

Update auf ein Dokument, das noch nicht existieren darf:

 curl -X POST --header "Content-Type: application/json" "http://localhost:8983/solr/videothek/update" -d '
 [{"id" : "movie_2",
   "name" : "Grüner Planet",
   "count" : 2,
   "genre" : "SciFi",
   "_version_": -1
 }]'

Das Update wurde durchgeführt, da das Dokument noch nicht existierte.

 

Wir holen die aktuellen Versionen:

 curl "http://localhost:8983/solr/videothek/query?q=*:*&fl=id,_version_"
{
 "responseHeader":{ "status":0, "QTime":3,
 "params":{"q":"*:*","fl":"id,_version_"}},
"response":{"numFound":2,"start":0,"docs":[
   {"id":"movie_1", "_version_":1506773698028240896},
   {"id":"movie_2", "_version_":1506773749796438016}]
 }}

 

Update mit ungültiger erwarteter Versionnummer:

 curl -X POST --header "Content-Type: application/json" "http://localhost:8983/solr/videothek/update?versions=true" --data-binary '
 [ {"id" : "movie_2",
    "count": {"set" : 3},
    "_version_" : 9999 }]'
{"responseHeader":{"status":409,"QTime":33},
"error":{"msg":"version conflict for movie_2 expected=9999 actual=1506773749796438016","code":409}
}

Da unsere Version nicht der aktuellen Version entspricht, wurde dieses Update vom Server abgelehnt.

 

Update mit korrekter erwarteter Versionnummer:

 curl -X POST --header "Content-Type: application/json" "http://localhost:8983/solr/videothek/update?versions=true" --data-binary '
 [ {"id" : "movie_2",
    "count": {"set" : 3},
    "_version_" : 1506773749796438016 }]'
{"responseHeader":{"status":0,"QTime":27},"adds":["movie_2",1506773884971515904]}

Da wir die aktuelle Version ändern wollten, wurde das Update durchgeführt.

 

Die Version von Dokument movie_2 ist nun größer als bei unserem Update, da das Dokument verändert wurde.

 curl "http://localhost:8983/solr/videothek/query?q=*:*&fl=id,_version_"
{
"responseHeader":{"status":0,"QTime":17,
"params":{"q":"*:*","fl":"id,_version_"}},
 "response":{"numFound":2,"start":0,"docs":[
   {"id":"movie_1", "_version_":1506773698028240896},
   {"id":"movie_2", "_version_":1506773884971515904}]
 }}

 

Wie in den Beispielen ersichtlich wird, können diese beiden Varianten auch kombiniert werden, um atomare Updates nur bedingt durchzuführen.