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" }
};

await FieldResolverQueryVisitor.RunAsync(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 = await QueryValidator.ValidateAsync(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 geo field detection for geo queries
    config.IsGeoPointField = field => field == "location";

    // 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.