Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ TestResults/

docs/site
.cache

coveragereport
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## v2.2.1

Add:

- Added `src/test.sh` helper script to run the solution tests with XPlat code coverage and generate HTML, Markdown, and text coverage reports.
- Added regression tests in `BugDiscoveryTests` and `DiskVectorDatabaseTests` to cover paging metadata, empty database stream round-tripping, disk persistence reload behavior, immediate visibility of disk-backed writes, and immediate reopen-after-delete persistence.
- Added `CoverageExpansionTests` to extend coverage across model types, exception hierarchy, ID generators, `VectorTextResult`, `TextDataLoader`, `DatabaseFile`, memory store branches, and obsolete serialization wrappers.
- Added `DiskStoreRegressionTests` to validate disk-backed vocabulary and vector store serialization, deserialization, enumeration, and persistence behavior.

Fixed:

- Fixed `.Search()` / `.SearchAsync()` result paging metadata so `TotalPages` is calculated from the total result count instead of echoing the requested page size.
- Fixed `MemoryVectorDatabase<TMetadata>` deserialization from an empty binary stream so ID generation resets correctly instead of failing when no items exist.
- Fixed `BasicDiskVectorStore` read-after-write behavior so added items are immediately visible to `Count`, `GetIds()`, `ContainsKey()`, enumeration, and search before the background disk flush completes.
- Fixed `BasicDiskVectorStore` delete persistence to avoid WAL truncation races when a database is reopened immediately after delete operations.
- Fixed reopened disk-backed databases to correctly expose persisted IDs and search results after checkpoint recovery.
- Fixed `BasicDiskVocabularyStore.DeserializeFromJsonStreamAsync` so deserialized vocabularies restore the in-memory lookup cache as well as the persisted vocabulary map.
- Fixed `BasicDiskVectorStore.SerializeToJsonStreamAsync` to flush pending operations before serialization and persist actual vector items rather than only index offsets.
- Fixed `BasicDiskVectorStore.DeserializeFromJsonStreamAsync` to rebuild item storage, index, visible IDs, and cache from serialized vector items.
- Updated the package version to `2.2.1` and refreshed the copyright year range to `2024-2026`.

Notes:

- Added `coveragereport` to `.gitignore` to keep generated coverage artifacts out of source control.

## v2.2.0

Add:
Expand Down
4 changes: 2 additions & 2 deletions src/Build5Nines.SharpVector/Build5Nines.SharpVector.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
<PackageId>Build5Nines.SharpVector</PackageId>
<PackageProjectUrl>https://sharpvector.build5nines.com</PackageProjectUrl>
<RepositoryUrl>https://github.com/Build5Nines/SharpVector</RepositoryUrl>
<Version>2.2.0</Version>
<Version>2.2.1</Version>
<Description>Lightweight In-memory Vector Database to embed in any .NET Applications</Description>
<Copyright>Copyright (c) 2025 Build5Nines LLC</Copyright>
<Copyright>Copyright (c) 2024-2026 Build5Nines LLC</Copyright>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<Authors>Chris Pietschmann</Authors>
Expand Down
14 changes: 10 additions & 4 deletions src/Build5Nines.SharpVector/MemoryVectorDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,11 @@ public override async Task DeserializeFromBinaryStreamAsync(Stream stream)
{
await base.DeserializeFromBinaryStreamAsync(stream);

// Re-initialize the IdGenerator with the max Id value from the VectorStore
_idGenerator = new IntIdGenerator(VectorStore.GetIds().Max());
// Re-initialize the IdGenerator with the max Id value from the VectorStore when items exist.
var ids = VectorStore.GetIds().ToArray();
_idGenerator = ids.Length > 0
? new IntIdGenerator(ids.Max())
: new IntIdGenerator();
}

/// <summary>
Expand All @@ -71,7 +74,10 @@ public override void DeserializeFromBinaryStream(Stream stream)
{
base.DeserializeFromBinaryStream(stream);

// Re-initialize the IdGenerator with the max Id value from the VectorStore
_idGenerator = new IntIdGenerator(VectorStore.GetIds().Max());
// Re-initialize the IdGenerator with the max Id value from the VectorStore when items exist.
var ids = VectorStore.GetIds().ToArray();
_idGenerator = ids.Length > 0
? new IntIdGenerator(ids.Max())
: new IntIdGenerator();
}
}
24 changes: 18 additions & 6 deletions src/Build5Nines.SharpVector/VectorDatabaseBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,14 +249,20 @@ public async Task<IVectorTextResult<TId, TVocabularyKey, TMetadata>> SearchAsync
var totalCountFoundInSearch = similarities.Count();

IEnumerable<IVectorTextResultItem<TId, TVocabularyKey, TMetadata>> resultsToReturn;
if (pageCount != null && pageCount >= 0 && pageIndex >= 0) {
int totalPages;
if (pageCount != null && pageCount >= 0 && pageIndex >= 0)
{
resultsToReturn = similarities.Skip(pageIndex * pageCount.Value).Take(pageCount.Value);
} else {
totalPages = pageCount.Value == 0 ? 0 : (int)Math.Ceiling(totalCountFoundInSearch / (double)pageCount.Value);
}
else
{
// no paging specified, return all results
resultsToReturn = similarities;
totalPages = totalCountFoundInSearch > 0 ? 1 : 0;
}

return new VectorTextResult<TId, TVocabularyKey, TMetadata>(totalCountFoundInSearch, pageIndex, pageCount.HasValue ? pageCount.Value : 1, resultsToReturn);
return new VectorTextResult<TId, TVocabularyKey, TMetadata>(totalCountFoundInSearch, pageIndex, totalPages, resultsToReturn);
}

private async Task<IEnumerable<IVectorTextResultItem<TId, TVocabularyKey, TMetadata>>> CalculateVectorComparisonAsync(TVocabularyKey queryText, float? threshold = null, Func<TMetadata?, Task<bool>>? filter = null)
Expand Down Expand Up @@ -673,14 +679,20 @@ public async Task<IVectorTextResult<TId, string, TMetadata>> SearchAsync(string
var totalCountFoundInSearch = similarities.Count();

IEnumerable<IVectorTextResultItem<TId, string, TMetadata>> resultsToReturn;
if (pageCount != null && pageCount >= 0 && pageIndex >= 0) {
int totalPages;
if (pageCount != null && pageCount >= 0 && pageIndex >= 0)
{
resultsToReturn = similarities.Skip(pageIndex * pageCount.Value).Take(pageCount.Value);
} else {
totalPages = pageCount.Value == 0 ? 0 : (int)Math.Ceiling(totalCountFoundInSearch / (double)pageCount.Value);
}
else
{
// no paging specified, return all results
resultsToReturn = similarities;
totalPages = totalCountFoundInSearch > 0 ? 1 : 0;
}

return new VectorTextResult<TId, string, TMetadata>(totalCountFoundInSearch, pageIndex, pageCount.HasValue ? pageCount.Value : 1, resultsToReturn);
return new VectorTextResult<TId, string, TMetadata>(totalCountFoundInSearch, pageIndex, totalPages, resultsToReturn);
}

private async Task<IEnumerable<IVectorTextResultItem<TId, string, TMetadata>>> CalculateVectorComparisonAsync(string queryText, float? threshold = null, Func<TMetadata?, Task<bool>>? filter = null)
Expand Down
Loading
Loading