![]() |
VOOZH | about |
dotnet add package Iciclecreek.Lucene.Net.Linq --version 4.8.5-beta00017
NuGet\Install-Package Iciclecreek.Lucene.Net.Linq -Version 4.8.5-beta00017
<PackageReference Include="Iciclecreek.Lucene.Net.Linq" Version="4.8.5-beta00017" />
<PackageVersion Include="Iciclecreek.Lucene.Net.Linq" Version="4.8.5-beta00017" />Directory.Packages.props
<PackageReference Include="Iciclecreek.Lucene.Net.Linq" />Project file
paket add Iciclecreek.Lucene.Net.Linq --version 4.8.5-beta00017
#r "nuget: Iciclecreek.Lucene.Net.Linq, 4.8.5-beta00017"
#:package Iciclecreek.Lucene.Net.Linq@4.8.5-beta00017
#addin nuget:?package=Iciclecreek.Lucene.Net.Linq&version=4.8.5-beta00017&prereleaseInstall as a Cake Addin
#tool nuget:?package=Iciclecreek.Lucene.Net.Linq&version=4.8.5-beta00017&prereleaseInstall as a Cake Tool
π Build and Test
π NuGet
Iciclecreek.Lucene.Net.Linq is a .NET library that enables LINQ queries to run natively on a Lucene.Net index.
This is a fork of the excellent library Lucene.Net.Linq which has been fully Modernized for Lucene.Net 4.x and .NET 8+
To install the Iciclecreek.Lucene.Net.Linq package, run the following command in the Package Manager Console
PM> Install-Package Iciclecreek.Lucene.Net.Linq
This branch ports the Lucene.Net.Linq library from the original Lucene.Net.Linq 3.0.3 /
net40 baseline onto Lucene.Net 4.8.0-beta00017 and SDK-style projects
multi-targeting netstandard2.0;net8.0;net10.0. Highlights:
Common.Logging.
Wire your logger via Lucene.Net.Linq.Util.Logging.LoggerFactory = myFactory; (defaults to NullLoggerFactory)..Similar() with automatic embedding at index and query timenetstandard2.0, net8.0,
and net10.0;[Field(DocValues = true)] /
[NumericField(DocValues = true)]). Writes a parallel column-store
field at index time so sorting, grouping and faceting read from a
packed forward index instead of uninverting the inverted index via
FieldCache. Defaults to false on both attributes β opt in per
field where sort/group performance matters. Silently ignored on
collection (IEnumerable<T>) properties because Lucene.Net 4.8
beta lacks SortedNumericDocValuesField..Similar() with automatic embedding at index and query time[Field(Default = true)]) β one property serves as the target for both free-text Query() and vector Similar() when no field is specified.Query() β embed wildcards, boolean operators, phrase queries and more directly in LINQ expressionsprovider.AsQueryable<T>("query string")TermsFiltercollection.Contains(field) β the LINQ "IN" pattern translates to an efficient TermsFilterTermFreqVectorDocumentMapper<T> for fields indexed with TermVector = TermVectorMode.YesIciclecreek.Lucene.Net.Linq maps plain CLR objects (POCOs) onto Lucene
Documents. You can describe the mapping in two ways: with attributes
on the type, or with a fluent code-first builder. Both produce the
same internal IFieldMapper<T> graph and are fully interchangeable.
The simplest case: decorate properties with [Field] or
[NumericField]. Properties without an attribute are still mapped
with sensible defaults (analyzed, stored, OR query operator).
[DocumentKey(FieldName = "Type", Value = "Article")]
public class Article
{
[Field(Key = true)]
public string Id { get; set; }
[Field(IndexMode.Analyzed, DocValues = true)]
public string Title { get; set; }
[Field(IndexMode.NotAnalyzed)]
public string Slug { get; set; }
[NumericField]
public int WordCount { get; set; }
[NumericField(DocValues = true)]
public DateTime PublishedAt { get; set; }
[Field("body_text", Analyzer = typeof(EnglishAnalyzer))]
public string Body { get; set; }
public IList<string> Tags { get; set; } // multi-valued, default mapping
[QueryScore]
public float Score { get; set; } // populated at read time
[IgnoreField]
public string TransientUiState { get; set; }
}
[Field] options| Option | Default | Notes |
|---|---|---|
Field (ctor) |
property name | Backing Lucene field name. |
IndexMode (ctor) |
Analyzed |
Analyzed, NotAnalyzed, NotAnalyzedNoNorms, NoIndex. |
Store |
Yes |
Whether the original value is kept verbatim for read-back. |
Key = true |
false |
Participates in the document primary key (used for replace/delete). Multiple properties can be marked. |
Default = true |
false |
Marks this property as the default search property. Used by .Query(), .Similar(), and AsQueryable<T>(string) when no field is specified. Only one property per type should be marked. When not set, defaults to the first key or first indexed property. |
Boost |
1.0f |
Index-time boost. |
Converter |
derived | Custom TypeConverter for non-string values. |
Format |
yyyy-MM-ddTHH:mm:ss for DateTime |
Format string used by the default value-type converter. Ignored if Converter is set. |
CaseSensitive |
depends on converter | Disables LowercaseExpandedTerms in the query parser; also routes the field through KeywordAnalyzer instead of CaseInsensitiveKeywordAnalyzer when no explicit analyzer is set. |
DefaultParserOperator |
OR |
OR or AND for parsed-string queries on this field. |
Analyzer |
null |
Per-field analyzer type; must have a default ctor or one accepting LuceneVersion. Overridden by an analyzer passed to LuceneDataProvider. |
TermVector |
No |
No, Yes, WithPositions, WithOffsets, WithPositionsOffsets. |
NativeSort |
false |
When true, sort uses byte-wise string comparison instead of IComparable.CompareTo. Only meaningful when the converter's string output is alphanumerically sortable. |
DocValues |
false |
Write a parallel SortedDocValuesField column. Opt in for fast sort/group/facet on hot fields. |
[NumericField] optionsTrie-encoded numeric field. Use this for int, long, float,
double, enum (via underlying integral type), DateTime /
DateTimeOffset (via the built-in ticks converter), bool (via a
custom converter), or any other type your TypeConverter can map onto
one of the four primitive numeric types.
| Option | Default | Notes |
|---|---|---|
Field (ctor) |
property name | Backing Lucene field name. |
Store |
Yes |
|
Key |
false |
|
Boost |
1.0f |
Silently dropped β Lucene 4.8 numeric fields don't index norms. |
Converter |
built-in for DateTime/DateTimeOffset |
TypeConverter mapping the property type to one of int/long/float/double. |
PrecisionStep |
NumericUtils.PRECISION_STEP_DEFAULT |
Trie granularity. Smaller = faster range queries, larger index. |
DocValues |
false |
Writes a parallel NumericDocValuesField / SingleDocValuesField / DoubleDocValuesField column. Opt in for fast sort. |
[IgnoreField] β exclude a public property from mapping entirely.[QueryScore] β populate a float property with the document's
relevance score on read. No options.[DocumentKey(FieldName, Value)] (class-level, repeatable) β
pins a constant field/value on every document of the class. Useful
for adding fixed metadata fields.When you can't or don't want to put attributes on the type β DTOs from
another assembly, generated code, schemas built at runtime β use
ClassMap<T>:
public class ArticleMap : ClassMap<Article>
{
public ArticleMap() : base(LuceneVersion.LUCENE_48)
{
Key(a => a.Id);
Property(a => a.Title).AnalyzedWith(new StandardAnalyzer(LuceneVersion.LUCENE_48));
Property(a => a.Slug).NotAnalyzed();
NumericField(a => a.WordCount);
NumericField(a => a.PublishedAt).WithPrecisionStep(8);
Property(a => a.Body).WithFieldName("body_text");
}
}
var provider = new LuceneDataProvider(directory, LuceneVersion.LUCENE_48);
provider.RegisterCacheWarmingCallback<Article>(...);
using var session = provider.OpenSession(new ArticleMap());
The fluent and attribute paths are equivalent β you can mix them in a single project, but not on the same type. The fluent API exposes one method per option in the tables above.
Anything Lucene can store is ultimately a string (text fields) or a
trie-coded primitive (numeric fields). For everything else, supply a
TypeConverter:
public class VersionConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext c, Type t) => t == typeof(string);
public override bool CanConvertTo(ITypeDescriptorContext c, Type t) => t == typeof(string);
public override object ConvertFrom(ITypeDescriptorContext c, CultureInfo i, object v) => Version.Parse((string)v);
public override object ConvertTo(ITypeDescriptorContext c, CultureInfo i, object v, Type t) => v?.ToString();
}
public class Package
{
[Field(Converter = typeof(VersionConverter))]
public Version Version { get; set; }
}
For [NumericField], the converter must be able to convert the
property type to one of long, int, double, or float. The
built-in DateTimeToTicksConverter and DateTimeOffsetToTicksConverter
are wired up automatically when you mark a DateTime /
DateTimeOffset property as [NumericField].
Any property whose type implements IEnumerable<T> is treated as a
multi-valued field automatically. The element type drives the
underlying mapper, so IList<string> works with [Field] and
IList<int> with [NumericField]. Note that DocValues=true is
silently downgraded for collections in this Lucene.Net 4.8 beta β
SortedNumericDocValuesField isn't available, and
SortedSetDocValuesField doesn't fit single-value LINQ ordering.
[Field(Key = true)] on one or more properties marks a primary
key β a unique identifier within a document type. Adding a document
whose key collides with an existing one replaces the old document
in a single atomic transaction.
The library automatically supports inheritance hierarchies.
This means if you have class Dog : Animal and class Cat : Animal:
// Store mixed subtypes through a base-type session
using (var session = provider.OpenSession<Animal>())
{
session.Add(new Dog { Id = "1", Name = "Rex", Breed = "Shepherd" });
session.Add(new Cat { Id = "2", Name = "Whiskers", Indoor = true });
session.Add(new GuideDog { Id = "3", Name = "Buddy", Breed = "Lab", Handler = "John" });
session.Commit();
}
// Query by base type β returns all three, each as its real type
var animals = provider.AsQueryable<Animal>().ToList();
// animals[0] is Dog, animals[1] is Cat, animals[2] is GuideDog
// Query by middle type β returns Dog + GuideDog, not Cat
var dogs = provider.AsQueryable<Dog>().ToList();
// Query by leaf type β returns only GuideDog
var guides = provider.AsQueryable<GuideDog>().ToList();
Key behaviors:
Dog
stored via OpenSession<Animal>() retains its Breed property. When
read back, Breed is populated even when querying as Animal.Dog
through ISession<Animal> and change Dog.Breed, the session
detects the modification and flushes it on commit.Dog and a Cat share the same
[Field(Key = true)] value, the last write wins β they are treated
as the same document.Activator.CreateInstance to instantiate the actual runtime type
when reading polymorphic documents.Once you have a session, provider.AsQueryable<T>() returns an
IQueryable<T> that translates standard LINQ operators into Lucene
queries. Most of LINQ-to-objects works; the parts that don't, throw
at translation time with a clear message.
| LINQ | Lucene equivalent |
|---|---|
Where(d => d.Field == value) |
TermQuery |
Where(d => d.Field != value) |
Boolean MUST_NOT |
Where(d => d.Field.StartsWith("foo")) |
PrefixQuery |
Where(d => d.Field.EndsWith("foo")) |
WildcardQuery (*foo) |
Where(d => d.Field.Contains("foo")) |
WildcardQuery (*foo*) |
Where(d => d.Numeric > 5) etc. |
NumericRangeQuery |
Where(d => d.Field == null) |
Negated existence query |
&&, \|\|, ! |
Boolean MUST / SHOULD / MUST_NOT |
OrderBy, OrderByDescending, ThenBy, ThenByDescending |
Multi-field Sort |
Skip(n).Take(m) |
IndexSearcher.Search(query, n + m) window |
First / FirstOrDefault / Single / SingleOrDefault |
Take(1) |
Any() / Any(predicate) |
TotalHits > 0 |
Count() / LongCount() |
TotalHits |
Min / Max |
Sort ascending/descending + Take(1) |
Where(d => d.Field.Query("text*")) |
Parsed Lucene query on a specific field |
Where(d => d.Query("text*")) |
Parsed Lucene query on default search property |
Where(d => d.Field.Similar("text")) |
VectorQuery on field (KNN or cosine similarity) |
Where(d => d.Similar("text")) |
VectorQuery on default search property |
Select(d => new { ... }) |
Document projection (read only the fields you reference) |
The LINQ collection.Contains(field) pattern translates to an efficient
TermsFilter -- a single-pass filter that matches documents whose field
value appears in the collection. This is the Lucene equivalent of SQL's
IN operator.
var allowedCategories = new[] { "tech", "science", "health" };
var articles = provider.AsQueryable<Article>()
.Where(a => allowedCategories.Contains(a.Category))
.ToList();
This produces ConstantScoreQuery(TermsFilter([Category:tech, Category:science, Category:health])) -- much more efficient than chaining
|| equality checks, especially for large collections. Works with
arrays, lists, and any IEnumerable<T>, including captured variables.
You can combine it with other predicates:
var results = provider.AsQueryable<Article>()
.Where(a => allowedCategories.Contains(a.Category) && a.WordCount > 500)
.ToList();
An empty collection matches nothing (returns zero results).
LINQ join syntax works across document types. The library materializes
both sides via separate Lucene searches and joins them in memory. A
semi-join optimization uses TermsFilter to push the outer key values
into the inner query, so only matching inner documents are fetched.
var articles = provider.AsQueryable<Article>();
var authors = provider.AsQueryable<Author>();
// Single join
var results = (
from article in articles
join author in authors on article.AuthorId equals author.Username
select new { article.Title, author.DisplayName }
).ToList();
Multiple joins chain naturally:
var categories = provider.AsQueryable<Category>();
var results = (
from article in articles
join author in authors on article.AuthorId equals author.Username
join category in categories on article.CategoryId equals category.Id
select new { article.Title, author.DisplayName, category.Label }
).ToList();
Where clauses on the outer side are pushed into Lucene before the join:
var results = (
from article in articles.Where(a => a.Title.Contains("lucene"))
join author in authors on article.AuthorId equals author.Username
select new { article.Title, author.DisplayName }
).ToList();
Method syntax also works:
var results = provider.AsQueryable<Article>()
.Join(
provider.AsQueryable<Author>(),
article => article.AuthorId,
author => author.Username,
(article, author) => new { article.Title, author.DisplayName })
.ToList();
How it works under the hood:
TermsFilter is built from those keys and pushed into the inner
query -- only inner documents matching an outer key are fetched.Enumerable.Join.This means joins are efficient when the outer result set is selective (few distinct keys), but will materialize both sides for broad queries. Lucene has no relational join engine -- this is a convenience that avoids manual materialization and in-memory joining in user code.
For cases where you need raw Lucene query syntax β wildcards, fuzzy search, boosting, range, fielded queries β there are three approaches, from simplest to most flexible:
One-liner β AsQueryable<T>(string) parses the query against the
type's DefaultSearchProperty and returns a filtered IQueryable<T>:
var results = provider.AsQueryable<Article>("kitten* OR dog*").ToList();
Inline in LINQ β .Query() embeds Lucene syntax directly in a
LINQ expression, composable with other predicates:
// Against the default search property
var results = provider.AsQueryable<Article>()
.Where(a => a.Query("kitten* OR dog*") && a.WordCount > 500)
.ToList();
// Against a specific property
var results = provider.AsQueryable<Article>()
.Where(a => a.Title.Query("lucene~0.8") && a.Category == "tech")
.ToList();
Pre-built Query object β for full control, use Where(Query):
var parser = provider.CreateQueryParser<Article>();
var query = parser.Parse("title:foo* AND year:[2020 TO 2024]");
var results = provider.AsQueryable<Article>().Where(query).ToList();
// Order by relevance β highest score first.
var top = documents
.WhereParseQuery("body:lucene")
.OrderByDescending(d => d.Score())
.Take(10);
// Per-query boost. Boost expressions can use document fields.
var boosted = documents
.WhereParseQuery("body:lucene")
.Boost(d => d.WordCount / 100.0f);
Score() is an extension on the document type, available inside
queries even when the POCO has no [QueryScore] property; mark a
property [QueryScore] to also have the score materialized onto each
returned object.
Skip(n).Take(m) translates to a Lucene window read. Lucene scores
the entire result set up to n + m and returns the requested slice,
so very large Skip values are slower than a key-set / cursor
approach β prefer cursoring on a sortable key field for deep paging.
OrderBy / OrderByDescending translate into Lucene SortFields.
The selection rules:
[NumericField] properties β typed numeric SortField (correct
numeric ordering).[Field] reference-type properties with a TypeConverter
implementing IComparable / IComparable<T> β custom comparator
that calls CompareTo, reading bytes from FieldCache.GetTerms.[Field] strings β SortFieldType.STRING.DocValues = true β typed SortField reading from
the column store, much faster on first touch.For hot sort fields, opt into DocValues:
[Field(DocValues = true)]
public string Title { get; set; }
[NumericField(DocValues = true)]
public DateTime PublishedAt { get; set; }
Without DocValues, the first sort on a field per segment uninverts
the inverted index via FieldCache β correct, but pays an O(n) cost
on first touch and holds the result in memory. With DocValues, the
sort reads directly from a packed column.
var directory = FSDirectory.Open(new DirectoryInfo("./index"));
var provider = new LuceneDataProvider(directory, LuceneVersion.LUCENE_48);
using (var session = provider.OpenSession<Article>())
{
session.Add(new Article { Id = "1", Title = "Hello" });
var hits = session.Query()
.Where(a => a.Title == "hello")
.ToList();
foreach (var doc in hits) doc.Title = doc.Title.ToUpperInvariant();
session.Commit(); // single atomic flush; rollback on exception
}
Key points:
Commit() flushes
them in a single atomic transaction; Dispose() without Commit()
rolls them back.Add on a document whose [Field(Key = true)] matches an existing
key replaces the existing document, in the same transaction.Delete(key) and DeleteAll() are also queued and flushed at
commit time.provider.AsQueryable<T>() opens a read-only view that doesn't need
a session β convenient for read-only OData / API endpoints.Lucene reopens its IndexSearcher periodically as the index changes.
You can register queries to run on the new searcher before it
becomes visible, so the first user query doesn't pay the warm-up
cost:
provider.RegisterCacheWarmingCallback<Article>(queryable =>
{
queryable.OrderByDescending(a => a.PublishedAt).Take(50).ToList();
});
Wire up a logger factory once, at startup:
Lucene.Net.Linq.Util.Logging.LoggerFactory = LoggerFactory.Create(b =>
b.AddConsole().SetMinimumLevel(LogLevel.Debug));
The library logs query translation steps, cache reload events, and
session commit summaries. Defaults to NullLoggerFactory if you
don't set one.
The library supports vector similarity search. String properties can
opt in via [VectorField] or the fluent .AsVectorField() API. Embeddings
are automatically computed at index time and ranked by similarity at
query time using .Similar().
Vector search requires an IEmbeddingGenerator<string, Embedding<float>>
(from Microsoft.Extensions.AI). Any implementation works --
OpenAI, Azure OpenAI, Ollama, or a local model. For local / offline
scenarios, ElBruno.LocalEmbeddings
is a good choice -- under 20 MB and works great offline:
using ElBruno.LocalEmbeddings;
using ElBruno.LocalEmbeddings.Options;
var generator = new LocalEmbeddingGenerator(new LocalEmbeddingsOptions
{
ModelName = "SmartComponents/bge-micro-v2",
PreferQuantized = true
});
var provider = new LuceneDataProvider(directory, LuceneVersion.LUCENE_48);
provider.Settings.EmbeddingGenerator = generator;
Add [VectorField] alongside [Field] on any string property:
public class Article
{
[Field(Key = true)]
public string Id { get; set; }
[Field, VectorField]
public string Title { get; set; }
[Field]
public string Category { get; set; }
}
A common pattern is a compound property that combines multiple text fields into one search surface, marked as both the default search property and a vector field:
public class Article
{
[Field(Key = true)]
public string Id { get; set; }
[Field]
public string Title { get; set; }
[Field]
public string Body { get; set; }
[Field(Default = true), VectorField]
public string Content => $"{Title} {Body}";
}
This makes Content the target for both article.Query("lucene*") (free-text)
and article.Similar("machine learning") (vector similarity).
public class ArticleMap : ClassMap<Article>
{
public ArticleMap() : base(LuceneVersion.LUCENE_48)
{
Key(a => a.Id);
Property(a => a.Title).AsVectorField();
DefaultProperty(a => a.Title);
}
}
.Similar()Property-level -- search against a specific field's embeddings:
var results = provider.AsQueryable<Article>()
.Where(a => a.Title.Similar("a cute cat napping"))
.Take(5)
.ToList();
Object-level -- search against the default search property
(set via [Field(Default = true)] or classMap.DefaultProperty()):
var results = provider.AsQueryable<Article>()
.Where(a => a.Similar("machine learning breakthroughs"))
.Take(5)
.ToList();
Hybrid -- .Similar() composes naturally with other predicates. Filters
are applied first, then matching documents are ranked by similarity:
// Only animals, ranked by similarity
var results = provider.AsQueryable<Article>()
.Where(a => a.Title.Similar("furry animals") && a.Category == "animals")
.Take(3)
.ToList();
Lucene.Net.Linq supports both WCF Data Services and WebApi OData. These libraries by default support a feature known as Null Propagation that adds null safety to LINQ Expressions to avoid NullReferenceException from being thrown when operating on a property that may be null.
A simple expression like:
from doc in Documents where doc.Name.StartsWith("Sample") select doc;
Is translated into:
from doc in Documents where (doc != null && doc.Name != null
&& doc.Name.StartsWith("Sample")) select doc;
Null Propagation is designed to work with LINQ To Objects but is not required for LINQ providers such as Lucene.Net.Linq. Lucene.Net.Linq does its best to remove these null-safety checks when translating a LINQ expression tree into a Lucene Query, but for best performance it is recommended to simply turn the feature off, as in this example:
public class PackagesODataController : ODataController
{
[EnableQuery(HandleNullPropagation = HandleNullPropagationOption.False)]
public IQueryable<Package> Get()
{
return provider.AsQueryable<Package>();
}
}
See Issues on the GitHub project page.
Some characters, even when using a KeywordAnalyzer or equivalent, will
not be handled correctly by Lucene.Net.Linq, such as \, :, ? and *
because these characters have special meaning to Lucene's query parser.
This means if you want to index a DOS style path such as c:\dos and
later retrieve documents using the same term, it will not work properly.
These characters are perfectly fine for fields that will be analyzed by a tokenizer that would remove them, but exact matching on the entire value is not possible.
If exact matching is required, these characters should be replaced with suitable substitutes that are not reserved by Lucene.
A handful of subsystems shifted because the underlying Lucene API was removed or substantially reworked between 3.0.3 and 4.8:
[DocumentBoost] attribute and the document
boost read/write hooks have been deleted. Equivalent functionality
must be expressed as field-level boost or as a custom scoring query.Int32Field/Int64Field/etc. has
no effect. The Boost property on [NumericField] is accepted for
source compatibility but silently ignored at index time.IComparable / IComparable<T> and has a
TypeConverter (e.g. System.Version) sort correctly via
GenericConvertableFieldComparatorSource /
NonGenericConvertableFieldComparatorSource, which read field bytes
through FieldCache.GetTerms. Value-type properties (int, bool,
DateTime, nullables) cannot use this path because Lucene 4.8's
FieldComparer<T> constrains T : class β mark them [NumericField]
for true numeric ordering, otherwise they fall back to string sort.MergePolicyBuilder is now a Func<MergePolicy> returning the
policy to install. Lucene 4.8 requires the merge policy to be set on
IndexWriterConfig before the writer is constructed, so the old
delegate signature that received the live IndexWriter no longer
fits.Iciclecreek.Lucene.Net.Linq 4.x is source-compatible for the most common usage
shape β annotated POCOs, LuceneDataProvider, OpenSession, LINQ
queries β but the underlying Lucene 3 β 4.8 jump forces a few changes.
Index files are not compatible. Lucene 4.x cannot read 3.x segments
at all. Plan to reindex from your source of truth, or run a one-time
upgrade through Lucene's IndexUpgrader 3 β 4 path before swapping
libraries. Run reindexing in a separate utility against an empty
directory; do not point the new library at an old index.
Step-by-step:
Replace the package reference:
<PackageReference Include="Iciclecreek.Lucene.Net.Linq" Version="4.8.0-beta00017" />
The package id changed from Lucene.Net.Linq to Iciclecreek.Lucene.Net.Linq
to disambiguate this fork from the dormant original.
Retarget. The library is netstandard2.0;net8.0;net10.0. .NET Framework 4.6.1+
consumers are supported via netstandard2.0; net40βnet46 consumers are
not.
Update your LuceneVersion constants. Replace
Lucene.Net.Util.Version.LUCENE_30 with
Lucene.Net.Util.LuceneVersion.LUCENE_48 everywhere. The type was
renamed in Lucene.Net 4.8.
Fix any direct Lucene.Net.* usage. The bulk of the porting effort
is in the underlying Lucene 3 β 4.8 namespace and API churn, not in
this library:
Lucene.Net.QueryParsers β Lucene.Net.QueryParsers.Classic
Lucene.Net.Analysis.Standard.StandardAnalyzer now lives in
Lucene.Net.Analysis.Standard; many tokenizers moved into
Lucene.Net.Analysis.Core.
Field constructors now take a FieldType instead of separate
Store/Index/TermVector enums. The library still accepts
StoreMode / IndexMode / TermVectorMode on [Field]; only
direct new Field(...) callers need to change.
IndexWriter now requires an IndexWriterConfig:
var config = new IndexWriterConfig(LuceneVersion.LUCENE_48, analyzer);
var writer = new IndexWriter(directory, config);
IndexReader.Open β DirectoryReader.Open. Most session code
doesn't touch this directly.
Remove [DocumentBoost] attributes and any code that sets
Document.Boost. Document-level boost is gone in Lucene 4.8. If
you depended on it, fold the boost into a field boost on a
discriminator field, or apply it via a CustomScoreQuery.
Drop any boost set on [NumericField]. It's silently ignored
because numeric fields don't index norms in 4.8.
If you wrote a MergePolicyBuilder, change its signature from
the old delegate to Func<MergePolicy> and return the policy
instance directly. The library installs it on the
IndexWriterConfig before constructing the writer.
If you sort by a value-type property without [NumericField]
(e.g. a plain int or DateTime), be aware that 4.8 will sort
it lexicographically by string form. Add [NumericField] for
true numeric ordering, or accept the string sort if it happens
to match (e.g. ISO-8601 DateTime strings).
Replace Common.Logging wiring with
Microsoft.Extensions.Logging:
Lucene.Net.Linq.Util.Logging.LoggerFactory = myLoggerFactory;
Defaults to NullLoggerFactory if you don't set one.
(Optional) Opt into DocValues on hot sort fields by adding
DocValues = true to [Field] / [NumericField] attributes
on properties you OrderBy heavily.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 was computed. net5.0-windows net5.0-windows was computed. net6.0 net6.0 was computed. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 netcoreapp2.0 was computed. netcoreapp2.1 netcoreapp2.1 was computed. netcoreapp2.2 netcoreapp2.2 was computed. netcoreapp3.0 netcoreapp3.0 was computed. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 netstandard2.0 is compatible. netstandard2.1 netstandard2.1 was computed. |
| .NET Framework | net461 net461 was computed. net462 net462 was computed. net463 net463 was computed. net47 net47 was computed. net471 net471 was computed. net472 net472 was computed. net48 net48 was computed. net481 net481 was computed. |
| MonoAndroid | monoandroid monoandroid was computed. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen40 tizen40 was computed. tizen60 tizen60 was computed. |
| Xamarin.iOS | xamarinios xamarinios was computed. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. |
Showing the top 1 NuGet packages that depend on Iciclecreek.Lucene.Net.Linq:
| Package | Downloads |
|---|---|
|
LottaDB
LottaDB is a .NET library that stores POCO objects => Table Storage and Lucene catalogs with all of the goodness of LINQ. |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.8.5-beta00017 | 205 | 5/11/2026 |
| 4.8.4-beta00017 | 167 | 4/23/2026 |
| 4.8.3-beta00017 | 70 | 4/22/2026 |
| 4.8.2-beta00017 | 67 | 4/20/2026 |
| 4.8.1-beta00017 | 108 | 4/15/2026 |
| 4.8.0-beta00017 | 70 | 4/8/2026 |