Skip to content

Getting Started ​

Foundatio.Lucene is a library for parsing Lucene-style query strings and converting them to various output formats. This guide will walk you through the basic setup and your first query.

Installation ​

Install the NuGet packages for your use case:

bash
dotnet add package Foundatio.Lucene
bash
dotnet add package Foundatio.Lucene.EntityFramework
bash
dotnet add package Foundatio.Lucene.Elasticsearch

Basic Parsing ​

The simplest usage is to parse a query string into an AST:

csharp
using Foundatio.Lucene;

var result = LuceneQuery.Parse("title:hello AND status:active");

if (result.IsSuccess)
{
    var document = result.Document; // QueryDocument (root AST node)
    Console.WriteLine($"Parsed {document.Children.Count} clauses");
}
else
{
    // Handle errors - partial AST may still be available
    foreach (var error in result.Errors)
    {
        Console.WriteLine($"Error at {error.Line}:{error.Column}: {error.Message}");
    }
}

Converting Back to Query String ​

You can convert the AST back to a query string:

csharp
using Foundatio.Lucene;

var result = LuceneQuery.Parse("title:test AND (status:active OR status:pending)");
var queryString = QueryStringBuilder.ToQueryString(result.Document);
// Returns: "title:test AND (status:active OR status:pending)"

Field Aliasing ​

Map user-friendly field names to your actual data model:

csharp
using Foundatio.Lucene;
using Foundatio.Lucene.Visitors;

var result = LuceneQuery.Parse("user:john AND created:[2020-01-01 TO 2020-12-31]");

var fieldMap = new FieldMap
{
    { "user", "account.username" },
    { "created", "metadata.timestamp" }
};

FieldResolverQueryVisitor.Run(result.Document, fieldMap);

var resolved = QueryStringBuilder.ToQueryString(result.Document);
// Returns: "account.username:john AND metadata.timestamp:[2020-01-01 TO 2020-12-31]"

Query Validation ​

Restrict what users can query:

csharp
using Foundatio.Lucene;

var result = LuceneQuery.Parse("*wildcard AND title:test");

var options = new QueryValidationOptions
{
    AllowLeadingWildcards = false
};
options.AllowedFields.Add("title");
options.AllowedFields.Add("status");

var validationResult = QueryValidator.Validate(result.Document, options);

if (!validationResult.IsValid)
{
    Console.WriteLine(validationResult.Message);
}

Entity Framework Integration ​

Enable dynamic, user-driven queries in your API endpoints:

csharp
using Foundatio.Lucene.EntityFramework;

// In your API controller or service
[HttpGet("employees")]
public async Task<IActionResult> SearchEmployees([FromQuery] string query)
{
    var parser = new EntityFrameworkQueryParser();

    // User provides: "name:john AND salary:[50000 TO *] AND department:engineering"
    var filter = parser.BuildFilter<Employee>(query);

    var results = await _context.Employees
        .Where(filter)
        .ToListAsync();

    return Ok(results);
}

With field aliasing to protect your data model:

csharp
var parser = new EntityFrameworkQueryParser();

// Map user-friendly names to actual entity properties
var fieldMap = new FieldMap
{
    { "name", "FullName" },
    { "dept", "Department.Name" },
    { "hired", "HireDate" }
};

// User query: "name:john AND dept:engineering AND hired:[2020-01-01 TO *]"
var filter = parser.BuildFilter<Employee>(userQuery, fieldMap);

Elasticsearch Integration ​

Generate Elasticsearch Query DSL from Lucene syntax:

csharp
using Foundatio.Lucene.Elasticsearch;
using Elastic.Clients.Elasticsearch;

var parser = new ElasticsearchQueryParser();

// Build an Elasticsearch Query from a Lucene query string
var query = parser.BuildQuery("title:hello AND status:active");

// Use with the Elasticsearch client
var client = new ElasticsearchClient();
var response = await client.SearchAsync<Document>(s => s
    .Index("my-index")
    .Query(query)
);

With configuration options:

csharp
var parser = new ElasticsearchQueryParser(config =>
{
    // Use scoring queries (match) instead of filter queries (term)
    config.UseScoring = true;

    // Set default fields for unfielded terms
    config.DefaultFields = ["title", "content"];

    // Map user-friendly field names
    config.FieldMap = new FieldMap
    {
        { "author", "metadata.author" },
        { "created", "metadata.timestamp" }
    };

    // Configure date field detection for date ranges
    config.IsDateField = field => field.EndsWith("date") || field.EndsWith("timestamp");
    config.DefaultTimeZone = "America/Chicago";
});

var query = parser.BuildQuery("author:john AND created:[2024-01-01 TO now]");

Next Steps ​

Now that you have the basics working, explore more advanced features:

Released under the Apache 2.0 License.