.NET client¶
The LuceneServerClient class is part of a .NET Standard library and provides easy access to the REST API of LuceneServer.NET.
The class is in the LuceneServerNET.Client.dll and can be added to existing projects via nuget.org. The source code is also included in
this repository.
Instantiation¶
A client can be instantiated as follows:
using(var client = new LuceneServerClient(serverUrl, indexName))
{
// client operations
}
The client should be created with 'using' when changes are made to the index within the client (create or delete document).
Dispose() ensures that the index on the server is refreshed and that all changes become visible. If the client is used only for querying,
using can also be omitted.
A client always refers to one index. The corresponding index can be specified via the indexName.
To communicate with the LuceneServerNET, the client uses an instance of System.Net.Http.HttpClient. Each instance creates a new HttpClient.
If an existing HttpClient is used, it can be passed as an additional parameter during instantiation:
using(var client = new LuceneServerClient(serverUrl, indexName, myHttpClient))
{
// client operations
}
This ensures that, for example, Authorization headers set on that HttpClient are passed to the server.
Authentication¶
If the server requires Basic authentication, pass the client id and secret:
using(var client = new LuceneServerClient(serverUrl, indexName, "importer", "my-secret"))
{
// client operations
}
Alternatively the credentials can be part of the URL: https://importer:my-secret@search.example.com.
Methods¶
The following methods are used for index management:
// Check if index exists
if (await client.IndexExistsAsync())
{
// if true: remove existing index
await client.RemoveIndexAsync();
}
// create a new index
await client.CreateIndexAsync();
To map an index, a MappingIndex object must first be created. This then specifies the fields and primary search fields. The MapAsync function sets the mapping for the index:
var mapping = new IndexMapping()
{
// primary search field for the index
PrimaryFields = new string[] { "title", "content" },
Fields = new FieldMapping[]
{
// create _guid field, so that documents can be queried or deleted individually
new DocumentGuidField(),
// Indexed (queryable) fields, default-type: FieldTypes.TextType
new IndexField("title"),
new IndexField("content"),
new IndexField("feed_id", FieldTypes.StringType),
new IndexField("publish_date", FieldTypes.DateTimeType),
// stored (not queryable) fields
new StoredField("url"),
new StoredField("image"),
}
};
// Map
await client.MapAsync(mapping);
To index documents, they must be passed to the client as IEnumerable<IDictionary<string, object>>.
A value for the DocumentGuid field _guid is not passed here, but is set automatically by the server.
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
});
The following methods can be used to delete documents:
// delete by _guid: Task<bool> RemoveDocumentsAsync(IEnumerable<Guid> guids)
await client.RemoveDocumentsAsync(new Guid[] { guid_doc1, guid_doc2 /*, ...*/ });
// delete by query term: Task<bool> RemoveDocumentsAsync(string field, string term)
await client.RemoveDocumentsAsync("title", "Batman"); // title contains Batman
The search is implemented in the function Task<LuceneSearchResult> SearchAsync(string query, IEnumerable<string> outFields = null).
The Query syntax is the same as that of Lucene. Examples can be found here
Primary searches in the fields listed in the mapping. However, the Query syntax can also be used to search in all other indexed fields.
For simple full-text searches where the user enters keywords, it is recommended to adjust the input:
Input: Batm Robi
Query: +Batm* +Robi* => both search terms must occur (+), search with wildcard => all documents containing Batman and Robin are found.
The QueryBuilder class can be used to convert to this syntax:
var queryBuilder = new QueryBuilder();
var result = await client.SearchAsync($"{ queryBuilder.ParseTerm(term) }");
return result.Hits;
In addition to the term, you can specify the fields that should be returned.
For the outfields also special functions are available, see the REST API.
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);
Custom metadata¶
await client.AddCustomMetadataAsync("source", "{ \"db\": \"products\" }");
var meta = await client.GetCustomMetadataAsync("source");
var names = await client.GetCustomMetadataNamesAsync();
var all = await client.GetCustomMetadatasAsync();
Phonetic search and grouping¶
// phonetic search in the primary fields (no query syntax, plain term)
var result = await client.SearchPhoneticAsync("meier");
// distinct values of a field
var groups = await client.GroupAsync("feed_id");