Zum Inhalt

.NET-Client

Die LuceneServerClient Klasse ist Teil einer .NET Standard Bibliothek und bietet eine einfache Abstraktion für den Zugriff auf die REST-API von LuceneServer.NET.

Die Klasse befindet sich in der LuceneServerNET.Client.dll und kann über nuget.org in bestehende Projekte hinzugefügt werden. Der Sourcecode befindet sich ebenfalls in diesem Repository.

Instanzierung

Ein Client kann folgendermaßen instanziiert werden:

using(var client = new LuceneServerClient(serverUrl, indexName)) 
{
    // client operations
}

Der Client sollte in Verbindung mit using verwendet werden, wenn innerhalb des Clients Änderungen am Index vorgenommen werden (Dokument erzeugen oder löschen). Beim Dispose() wird gewährleistet, dass der Index am Server refreshed wird und alle Änderungen sichtbar werden. Wird der Client nur zum Abfragen verwendet, kann ein using auch entfallen.

Ein Client bezieht sich immer auf einen Index. Der entsprechende Index kann über den indexName angegeben werden.

Für die Kommunikation mit den LuceneServerNET verwendet der Client eine Instanz von System.Net.Http.HttpClient. Bei jeder Instanziierung wird ein neuer HttpClient erzeugt. Sollte ein bestehender HttpClient verwendet werden, kann dieser als zusätzlicher Parameter bei der Instanziierung übergeben werden:

using(var client = new LuceneServerClient(serverUrl, indexName, myHttpClient)) 
{
    // client operations
}

Damit kann gewährleistet werden, dass beispielsweise am HttpClient gesetzte Authorization-Header an den Server übergeben werden.

Authentifizierung

Verlangt der Server Basic-Authentifizierung, werden Client-Id und Secret übergeben:

using(var client = new LuceneServerClient(serverUrl, indexName, "importer", "my-secret"))
{
    // client operations
}

Alternativ können die Zugangsdaten in der URL stehen: https://importer:my-secret@search.example.com.

Methoden

Folgende Methoden dienen zur Index Verwaltung:

// Überprüfen, ob Index existiert
if (await client.IndexExistsAsync())
{
    // wenn ja: bestehenden Index entfernen
    await client.RemoveIndexAsync();
}
// Index anlegen
await client.CreateIndexAsync();

Für das Mapping eines Index muss zuerst ein MappingIndex Objekt erzeugt werden. In diesem werden dann die Felder und primären Suchfelder angegeben. Mit der Funktion MapAsync wird das *Mapping" für den Index festgelegt:

var mapping = new IndexMapping()
{
    // In diesen Feldern wird standardmäßig gesucht
    PrimaryFields = new string[] { "title", "content" },
    Fields = new FieldMapping[]
    {
        // _guid Feld anlegen, damit Dokumente einzeln abgefragt oder gelöscht werden können
        new DocumentGuidField(),
        // Indizierte (suchbare) Felder, default-type: FieldTypes.TextType
        new IndexField("title"),
        new IndexField("content"),
        new IndexField("feed_id", FieldTypes.StringType),
        new IndexField("publish_date", FieldTypes.DateTimeType),
        // Gespeicherte (nicht suchbare) Felder
        new StoredField("url"),
        new StoredField("image"),
    }
}

// Map
await client.MapAsync(mapping);

Zum Indexieren von Document müssen diese als IEnumerable<IDictionary<string, object>> an den Client übergeben werden. Ein Wert für das DocumentGuid Feld _guid wird hier nicht übergeben, sondern wird vom Server automatisch gesetzt.

var doc = new Dictionary<string, object>();

doc["title"] = "My Documents Title";
doc["content"] = "Lorem Ipsum...";
doc["feed_id"] = "an_id_for_my_feed";
doc["publish_date"] = DateTime.Now;
doc["url"] = "https://my-article.com";
doc["image"] = "https://my-article.com/preview.jpg";

await client.IndexDocumentsAsync(new IDictionary<string, object>[] {
    doc
});

Für das Löschen von Dokumenten können folgende Methoden verwendet werden:

// Löschen über die _guid der Dokument: Task<bool> RemoveDocumentsAsync(IEnumerable<Guid> guids)
await client.RemoveDocumentsAsync(new Guid[] { guid_doc1, guid_doc2 /*, ...*/  });

// Löschen über Query-Term: Task<bool> RemoveDocumentsAsync(string field, string term)
await client.RemoveDocumentsAsync("title", "Batman");  // title contains Batman

Für die Suche steht die Funktion Task<LuceneSearchResult> SearchAsync(string query, IEnumerable<string> outFields = null) zur Verfügung. Die Query Syntax entspricht der von Lucene. Beispiele finden sich hier

Primär wird in den beim Mapping angeführten Felder gesucht. Über die Query Syntax kann allerdings auch in allen anderen indexierten Feldern gesucht werden.

Für einfache Volltextsuchen, bei denen der Anwender Schlagwörter eingibt, empfiehlt es sich, die Eingabe noch anzupassen:

Eingabe: Batm Robi

Query: +Batm* +Robi* => Beide Suchbegriffe müssen vorkommen (+), Suche mit Wildcard => alle Dokumente mit Batman und Robin werden gefunden

Für die Umwandlung in diese Syntax kann die QueryBuilder Klasse verwendet werden:

var queryBuilder = new QueryBuilder();
var result = await client.SearchAsync($"{ queryBuilder.ParseTerm(term) }");

return result.Hits;

Neben dem Term können auch noch die Felder übergeben werden, die zurückgegeben werden sollen. Für die Felder stehen, wie bei der REST-API beschrieben, zusätzlich Funktionen zur Verfügung:

var result = await client.SearchAsync(term,
                                      outFields: new[]
                                      {
                                        "*",  // all fields
                                        "content".SentencesWith("batman robin",1,1),
                                        "content".IncludedTerms("batman robin").As("incl")
                                      },
                                      size: 50,
                                      sortField: "publish_date",
                                      sortReverse: true);

Benutzerdefinierte Metadaten

await client.AddCustomMetadataAsync("source", "{ \"db\": \"products\" }");
var meta = await client.GetCustomMetadataAsync("source");
var names = await client.GetCustomMetadataNamesAsync();
var all = await client.GetCustomMetadatasAsync();

Phonetische Suche und Gruppierung

// phonetische Suche in den Primärfeldern (keine Query-Syntax, nur der Begriff)
var result = await client.SearchPhoneticAsync("meier");

// eindeutige Werte eines Feldes
var groups = await client.GroupAsync("feed_id");