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
68 changes: 67 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,72 @@ To delete a record from the search engine:
searchEngine.DeleteRecord(1);
```

### Pagination

In addition to basic searching, you can easily implement pagination using the `skip` and `limit` parameters in the `Search` method. This is especially useful when dealing with large datasets where you only want to display a subset of the results at a time.

```csharp
var resultsPage1 = searchEngine.Search(
search: "quick fox",
respectTokenOrder: true,
skip: 0, // Skip 0 records, start from the beginning
limit: 10 // Limit to 10 records in this page
);

var resultsPage2 = searchEngine.Search(
search: "quick fox",
respectTokenOrder: true,
skip: 10, // Skip the first 10 records
limit: 10 // Limit to 10 records in this page
);
```

### Adding Facets

To associate a facet (e.g., category, author) with a record:

```csharp
searchEngine.AddFacet(1, "category", "books");
searchEngine.AddFacet(1, "author", "John Doe");
```

### Deleting Facets

To remove a specific facet associated with a record:

```csharp
searchEngine.DeleteFacet(1, "category", "books");
searchEngine.DeleteFacet(1, "author", "John Doe");
```

### Faceted Search

If you're also filtering by facets, you can still apply pagination by specifying the `skip` and `limit` parameters:

```csharp
var facets = new Dictionary<string, string>
{
{ "category", "books" },
{ "author", "John Doe" }
};

var paginatedFacetedResultsPage1 = searchEngine.Search(
search: "quick fox",
facets: facets,
respectTokenOrder: true,
skip: 0, // Start from the first matching record
limit: 10 // Retrieve up to 10 records
);

var paginatedFacetedResultsPage2 = searchEngine.Search(
search: "quick fox",
facets: facets,
respectTokenOrder: true,
skip: 10, // Skip the first 10 matching records
limit: 10 // Retrieve the next 10 records
);
```

### Cleanup

When you're done using the search engine, ensure proper cleanup:
Expand Down Expand Up @@ -259,7 +325,7 @@ Future Search Engines: Additional search engines, such as Phrase Search Engine a

## License

ZoneTree.FullTextSearch is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details.
ZoneTree.FullTextSearch is licensed under the MIT License. See the [LICENSE](https://github.com/koculu/ZoneTree.FullTextSearch/blob/main/LICENSE) file for more details.

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
where TRecord : unmanaged
where TToken : unmanaged
{
/// <summary>
/// Gets or sets the facet previous token.
/// This property represents a token that precedes the current token in the context of facet-based searches.
/// </summary>
public TToken FacetPreviousToken { get; set; }

/// <summary>
/// Gets the primary zone tree used to store and retrieve records by token and previous token.
/// </summary>
Expand Down Expand Up @@ -70,7 +76,7 @@

readonly bool useSecondaryIndex;

bool isDropped = false;

Check warning on line 79 in src/ZoneTree.FullTextSaearch/Core/Index/IndexOfTokenRecordPreviousToken.cs

View workflow job for this annotation

GitHub Actions / build

Member 'isDropped' is explicitly initialized to its default value (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1805)

readonly SearchOnIndexOfTokenRecordPreviousToken<TRecord, TToken>
searchAlgorithm;
Expand Down Expand Up @@ -197,8 +203,8 @@
WaitForBackgroundThreads();
isDropped = true;
IsReadOnly = true;
ZoneTree1.Maintenance.DestroyTree();
ZoneTree2?.Maintenance.DestroyTree();
ZoneTree1.Maintenance.Drop();
ZoneTree2?.Maintenance.Drop();
ZoneTree1.Dispose();
ZoneTree2?.Dispose();
}
Expand Down Expand Up @@ -227,6 +233,32 @@
ZoneTree2.TryAdd(key, new());
}

/// <summary>
/// Deletes a record from the primary zone tree, and optionally from the secondary zone tree if a secondary index is enabled.
/// </summary>
/// <param name="token">The token associated with the record to delete.</param>
/// <param name="record">The record to be deleted.</param>
/// <param name="previousToken">The token that precedes the current token in the record.</param>
public void DeleteRecord(TToken token, TRecord record, TToken previousToken)
{
ThrowIfIndexIsDropped();
ZoneTree1.ForceDelete(new CompositeKeyOfTokenRecordPrevious<TRecord, TToken>()
{
Token = token,
Record = record,
PreviousToken = previousToken
});

if (!useSecondaryIndex)
return;

ZoneTree2.ForceDelete(new CompositeKeyOfRecordToken<TRecord, TToken>()
{
Record = record,
Token = token,
});
}

/// <summary>
/// Deletes a record from the index without using the secondary index.
/// </summary>
Expand Down Expand Up @@ -291,31 +323,61 @@
}

/// <summary>
/// Searches for records that match the provided tokens, with optional parameters for token order,
/// skipping records, and limiting the number of results.
/// Searches the index for records that match the specified tokens, with optional support for facets, token order respect, and pagination.
/// </summary>
/// <param name="tokens">The tokens to search for.</param>
/// <param name="firstLookAt">An optional token to prioritize during the search.</param>
/// <param name="respectTokenOrder">Indicates whether the order of tokens should be respected during the search.</param>
/// <param name="skip">The number of records to skip in the result set.</param>
/// <param name="limit">The maximum number of records to return.</param>
/// <returns>An array of records that match the search criteria.</returns>
/// <param name="tokens">
/// A read-only span of tokens that the records must contain. This parameter is mandatory unless facets are provided.
/// The tokens are logically grouped using "AND", meaning all tokens must be present in the matching records.
/// If both the tokens span and the facets span are empty, the result will be an empty array, as searching without tokens and facets is not supported.
/// Tokens can be empty if facets are provided; in this case, the search will be based solely on the facets.
/// To retrieve records without specific search tokens or facets, consider fetching them from the actual record source instead of using the search index.
/// </param>
/// <param name="firstLookAt">
/// An optional token that the search will prioritize when searching.
/// If not specified, the first token in the tokens span is used.
/// </param>
/// <param name="respectTokenOrder">
/// A boolean indicating whether the search should respect the order of tokens in the record.
/// If true, the records must contain the tokens in the specified order.
/// </param>
/// <param name="facets">
/// An optional read-only span of tokens that can be used to filter the search results.
/// If any facets are provided, records must contain at least one of these facet tokens to be included in the results.
/// If the span is empty or not provided, no facet filtering is applied, and all matching records are returned regardless of facet values.
/// </param>
/// <param name="skip">
/// The number of matching records to skip in the result set, useful for pagination.
/// Defaults to 0.
/// </param>
/// <param name="limit">
/// The maximum number of records to return, useful for limiting the result set size.
/// Defaults to 0, which indicates no limit.
/// </param>
/// <returns>
/// An array of records that match the specified tokens and facets, respecting the token order if specified.
/// The array may be empty if no matching records are found.
/// </returns>
public TRecord[] Search(
ReadOnlySpan<TToken> tokens,
TToken? firstLookAt = null,
bool respectTokenOrder = true,
ReadOnlySpan<TToken> facets = default,
int skip = 0,
int limit = 0)
{
return searchAlgorithm
.Search(tokens, firstLookAt, respectTokenOrder, skip, limit);
.Search(tokens, firstLookAt, respectTokenOrder, facets, skip, limit);
}

bool isDisposed = false;

Check warning on line 372 in src/ZoneTree.FullTextSaearch/Core/Index/IndexOfTokenRecordPreviousToken.cs

View workflow job for this annotation

GitHub Actions / build

Member 'isDisposed' is explicitly initialized to its default value (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1805)

/// <summary>
/// Disposes the resources used by the index.
/// </summary>
public void Dispose()
{
if (isDisposed) return;
isDisposed = true;
Maintainer1.WaitForBackgroundThreads();
Maintainer1.Dispose();
ZoneTree1.Dispose();
Expand Down
Loading
Loading