https://github.com/callumbwhyte/umbraco-search-extensions
Extensions for performing searches in Umbraco
https://github.com/callumbwhyte/umbraco-search-extensions
dotnet examine hacktoberfest lucene search search-engine umbraco
Last synced: 11 days ago
JSON representation
Extensions for performing searches in Umbraco
- Host: GitHub
- URL: https://github.com/callumbwhyte/umbraco-search-extensions
- Owner: callumbwhyte
- License: mit
- Created: 2020-06-23T21:42:13.000Z (almost 5 years ago)
- Default Branch: dev
- Last Pushed: 2024-06-07T08:27:00.000Z (11 months ago)
- Last Synced: 2025-03-26T07:15:18.513Z (28 days ago)
- Topics: dotnet, examine, hacktoberfest, lucene, search, search-engine, umbraco
- Language: C#
- Homepage:
- Size: 153 KB
- Stars: 26
- Watchers: 2
- Forks: 13
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Umbraco Search Extensions
[](https://www.nuget.org/packages/Our.Umbraco.Extensions.Search/)
## Getting started
This package is supported on Umbraco v9-v13 and v14
### Installation
Search Extensions is available via [NuGet](https://www.nuget.org/packages/Our.Umbraco.Extensions.Search/).
To install with the .NET CLI, run the following command:
$ dotnet add package Our.Umbraco.Extensions.Search
To install from within Visual Studio, use the NuGet Package Manager UI or run the following command:
PM> Install-Package Our.Umbraco.Extensions.Search
## Usage
### Querying
There are several short-hand extension methods for querying Umbraco content in an index – checking if an item is published, is visible, or has a template.
Querying only published content items can be done like this:
```
query.And().IsPublished()
```Similarly, querying all content where the `umbracoNaviHide` property is **not** set can be done like this:
```
query.And().IsVisible()
```It is possible to query content with a specific template ID set. If `0` or no value is passed to the method, the query will match content with **any** templatee ID set.
```
query.And().HasTemplate(int templateId)
```Finally, it is possible to query for content that has **any** one of the specified content type aliases. Out of the box Umbraco supports querying for a single content alias.
```
query.And().NodeTypeAlias(string[] aliases)
```### Cultures
Umbraco properties that have been set to "vary by culture" are indexed with a specific alias: `{fieldName}_{culture}`. For example, if the "pageTitle" field varies by culture and has 2 languages, English and Spanish, the index would contain 2 fields: `pageTitle_en` and `pageTitle_es`.
A culture can be passed to `Field` and `NodeName` queries like this:
```
query.And().Field(string field, string culture)query.And().NodeName(string nodeName, string culture)
```It even works with grouped queries such as `GroupedAnd`, `GroupedOr`, and `GroupedNot`, where multiple fields can be specified:
```
query.And().GroupedOr(string[] fields, string culture)
```### Searching
The `Page` extension methods efficiently get a given number of items *(`perPage`)* at a specific position *(`page`)* from Examine's `ISearchResults`. An optional type constraint can be added to also return paged results cast to `IPublishedContent`.
```
var query = searcher.CreatePublishedQuery();var searchResults = query.Execute();
var results = searchResults.Page(query, int page, int perPage, out int totalPages, out int totalResults);
```The total number of pages and results are exposed as an `out` parameter, but can be disgarded if not needed like so:
```
searchResults.Page(query, int page, int perPage, out _, out _);
```### Results
An entire results collection can be cast to a list of a given type like this:
```
var results = query.Execute().GetResults();
```Specific fields from an individual search result can be accessed via the `.Value()` extension method like this:
```
foreach (var result in query.Execute())
{
var value = result.Value(string field);
}
```### Advanced fields
Search Extensions introduces several new field types into Examine – `json`, `list`, `UDI` and `picker` – to ensure Umbraco data is correctly indexed and queryable.
Examine allows controlling an index's fields, field types, and [more](https://shazwazza.github.io/Examine/configuration#iconfigurenamedoptions), via [.NET's Named Options pattern](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options):
```
public class ConfigureIndexOptions : IConfigureNamedOptions
{
public void Configure(string name, LuceneDirectoryIndexOptions options)
{
if (name == "ExternalIndex")
{
options.FieldDefinitions.AddOrUpdate(new FieldDefinition("fieldName", "fieldType"));
}
}
}
```The options class must be registered in the [Dependency Injection](https://our.umbraco.com/documentation/reference/using-ioc/) container to apply:
```
builder.Services.ConfigureOptions();
```#### Core fields
Umbraco's "path" field is automatically indexed as a list and so a content item with the path `-1,1050,1100` can be queried like this:
```
query.Field("path", "1100");
```Umbraco's "createDate" and "updateDate" fields are automatically indexed as `date` values, whereas they would be regularly indexed as string values.
#### Pickers
The `picker` field type adds search-friendly aliases for the picked items into the index.
A picker with a selected a content item called "Example Page" can be queried like this:
```
query.Field("somePicker", "example-page");
```#### JSON
The `json` field type splits the properties of a JSON object into individual fields within the index.
Imagine a field called "locations" has the following JSON value:
```
[
{
"city": "London",
"position": {
"latitude": 51.5074,
"longitude": 0.1278
}
},
{
"city": "New York",
"position": {
"latitude": 40.7128,
"longitude": 74.0060
}
}
]
```Each property will be created as a field in the index, including any nested properties. In this example these would be called "locations_city", "locations_position_latitude" and "locations_position_longitude".
It is possible to index a subset of a JSON object's properties by supplying a path in [JSON Path format](https://www.newtonsoft.com/json/help/html/QueryJsonSelectTokenJsonPath.htm).
Register a new `ValueTypeFactory` in the index implementing the `json` type, and define the path as a parameter, before assigning it to a field:
```
public class ConfigureIndexOptions : IConfigureNamedOptions
{
public void Configure(string name, LuceneDirectoryIndexOptions options)
{
if (name == "ExternalIndex")
{
options.IndexValueTypesFactory = new Dictionary(options.IndexValueTypesFactory)
{
["position"] = new DelegateFieldValueTypeFactory(fieldName =>
{
return new JsonValueType(fieldName, "$[*].position");
};
};options.FieldDefinitions.AddOrUpdate(new FieldDefinition("locations", "position"));
}
}
}
```#### Multiple field types
There are advanced cases where indexing a value as multiple field types might be necessary, such as indexing different parts of the same JSON object into separately named fields or indexing specific properties within a JSON object as a defined type.
The `MultipleValueTypeFactory` assigns a chain of field types to a field and applies them in sequence:
```
public class ConfigureIndexOptions : IConfigureNamedOptions
{
public void Configure(string name, LuceneDirectoryIndexOptions options)
{
if (name == "ExternalIndex")
{
options.IndexValueTypesFactory = new Dictionary(options.IndexValueTypesFactory)
{
["locationData"] = new DelegateFieldValueTypeFactory(fieldName =>
{
return new MultipleValueTypeFactory(
fieldName,
new IIndexFieldValueType[]
{
new JsonValueType(x, "$[*].city"),
new JsonValueType("position", "$[*].position")
}
);
};
};options.FieldDefinitions.AddOrUpdate(new FieldDefinition("locations", "locationData"));
}
}
}
```In this example, the same "locations" JSON object will include all cities while an entirely new "position" field will be created including all latitudes and longitudes.
## Contribution guidelines
To raise a new bug, create an issue on the GitHub repository. To fix a bug or add new features, fork the repository and send a pull request with your changes. Feel free to add ideas to the repository's issues list if you would to discuss anything related to the library.
### Who do I talk to?
This project is maintained by [Callum Whyte](https://callumwhyte.com/) and contributors. If you have any questions about the project please get in touch on [Twitter](https://twitter.com/callumbwhyte), or by raising an issue on GitHub.
## Credits
The package logo uses the [Magnifying Glass](https://thenounproject.com/term/search/74453/) icon from the [Noun Project](https://thenounproject.com/) by [Rohith M S](https://thenounproject.com/rohithdezinr/), licensed under [CC BY 3.0 US](https://creativecommons.org/licenses/by/3.0/us/).
### A special #h5yr to our contributors
* [Busra Sengul](https://github.com/busrasengul)
* [Chriztian Steinmeier](https://github.com/greystate)
* [Dave Woestenborghs](https://github.com/dawoe)
* [Gareth Wright](https://github.com/garpunkal)
* [Jonas Pyfferoen](https://github.com/kows)## License
Copyright © 2024 [Callum Whyte](https://callumwhyte.com/), and other contributors
Licensed under the [MIT License](LICENSE.md).