Skill for configuring search and indexing in Orchard Core. Covers Lucene indexing, Elasticsearch integration, search settings, index definitions, and search queries.
You are an Orchard Core expert. Generate search and indexing configurations for Orchard Core.
IndexProfile for Lucene, Elasticsearch, and Azure AI Search indexes.OrchardCore.Lucene, OrchardCore.Elasticsearch, or OrchardCore.AzureAI.CreateOrUpdateIndexProfile step.IDocumentIndexHandler for document-wide indexing and read the source
record from BuildDocumentIndexContext.Record.DocumentIndex supports text, numeric, date, boolean, geo-point, Complex,
and Vector entries. Use the matching Set overload for the value type.IndexingPermissions.QuerySearchIndex permission. A
profile-specific QueryIndex_{name} permission is created by
IndexingPermissions.CreateDynamicPermission.{
"steps": [
{
"name": "Feature",
"enable": [
"OrchardCore.Search",
"OrchardCore.Lucene",
"OrchardCore.Indexing"
],
"disable": []
}
]
}
{
"steps": [
{
"name": "CreateOrUpdateIndexProfile",
"indexes": [
{
"Name": "Search",
"IndexName": "search",
"ProviderName": "Lucene",
"Type": "Content",
"Properties": {
"ContentIndexMetadata": {
"IndexLatest": false,
"IndexedContentTypes": [
"Article",
"BlogPost"
],
"Culture": "any"
},
"LuceneIndexMetadata": {
"AnalyzerName": "standardanalyzer",
"StoreSourceData": false
}
}
}
]
}
]
}
Configure in appsettings.json:
{
"OrchardCore_Elasticsearch": {
"Url": "https://localhost:9200",
"Ports": [9200],
"ConnectionType": "SingleNodeConnectionPool"
}
}
{
"steps": [
{
"name": "CreateOrUpdateIndexProfile",
"indexes": [
{
"Name": "Search",
"IndexName": "search",
"ProviderName": "Elasticsearch",
"Type": "Content",
"Properties": {
"ContentIndexMetadata": {
"IndexLatest": false,
"IndexedContentTypes": [
"Article",
"BlogPost"
],
"Culture": "any"
},
"ElasticsearchIndexMetadata": {
"AnalyzerName": "standard",
"StoreSourceData": false
}
}
}
]
}
]
}
{
"steps": [
{
"name": "Queries",
"Queries": [
{
"Source": "Lucene",
"Name": "RecentBlogPosts",
"Index": "Search",
"Template": "{\"query\":{\"bool\":{\"filter\":[{\"term\":{\"Content.ContentItem.ContentType\":\"BlogPost\"}}]}},\"sort\":{\"Content.ContentItem.CreatedUtc\":{\"order\":\"desc\"}},\"size\":10}",
"ReturnContentItems": true,
"Schema": "[]"
}
]
}
]
}
{% assign results = Queries.RecentBlogPosts | query %}
{% for item in results %}
<article>
<h2>{{ item.DisplayText }}</h2>
<p>{{ item.Content.BlogPost.Subtitle.Text }}</p>
</article>
{% endfor %}
using System.Collections.Generic;
using OrchardCore.Indexing;
using OrchardCore.Search.Abstractions;
public sealed class SearchService
{
private readonly IIndexProfileManager _indexProfileManager;
private readonly IEnumerable<ISearchService> _searchServices;
public SearchService(
IIndexProfileManager indexProfileManager,
IEnumerable<ISearchService> searchServices)
{
_indexProfileManager = indexProfileManager;
_searchServices = searchServices;
}
public async Task<IList<string>> SearchAsync(string query)
{
var indexProfile = await _indexProfileManager.FindByNameAsync("Search")
?? throw new InvalidOperationException("The Search index profile does not exist.");
var searchService = _searchServices.Single(service =>
string.Equals(service.Name, indexProfile.ProviderName, StringComparison.OrdinalIgnoreCase));
var result = await searchService.SearchAsync(indexProfile, query, 0, 10);
return result.ContentItemIds;
}
}
Implement IDocumentIndexHandler when indexing requires the complete source
record. BuildDocumentIndexContext.Record is an object, so check its type
before adding entries. The Complex and Vector overloads preserve structured
values and vector dimensions for providers that support them.
using System.Collections.Generic;
using OrchardCore.Indexing;
public sealed class ProductDocumentIndexHandler : IDocumentIndexHandler
{
public Task BuildIndexAsync(BuildDocumentIndexContext context)
{
if (context.Record is not ProductRecord record)
{
return Task.CompletedTask;
}
context.DocumentIndex.Set(
"Product.Name",
record.Name,
DocumentIndexOptions.Store);
context.DocumentIndex.Set(
"Product.Attributes",
record.Attributes,
DocumentIndexOptions.Store);
context.DocumentIndex.Set(
"Product.Embedding",
record.Embedding,
record.Embedding.Length,
DocumentIndexOptions.None);
return Task.CompletedTask;
}
}
public sealed class ProductRecord
{
public string Name { get; init; } = string.Empty;
public Dictionary<string, object> Attributes { get; init; } = [];
public float[] Embedding { get; init; } = [];
}
Register the handler as a scoped service:
services.AddScoped<IDocumentIndexHandler, ProductDocumentIndexHandler>();
Use ContentIndexingConstants from OrchardCore.Contents.Indexing for
built-in content index keys such as ContentTypeKey, ContentItemIdKey, and
FullTextKey.
IndexingPermissions.QuerySearchIndex is the centralized permission for
querying an index. The indexing authorization handler resolves the
profile-specific dynamic permission created by
IndexingPermissions.CreateDynamicPermission(indexProfile), whose name is
QueryIndex_{indexProfile.Name} and which is implied by
IndexingPermissions.ManageIndexes and IndexingPermissions.QuerySearchIndex.
using OrchardCore.Indexing;
public sealed class MyPartIndexHandler : ContentPartIndexHandler<MyPart>
{
public override Task BuildIndexAsync(
MyPart part,
BuildPartIndexContext context)
{
var options = DocumentIndexOptions.Store;
context.DocumentIndex.Set(
$"{nameof(MyPart)}.{nameof(MyPart.MyField)}",
part.MyField,
options);
return Task.CompletedTask;
}
}
public sealed class Startup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IContentPartIndexHandler, MyPartIndexHandler>();
}
}
{
"steps": [
{
"name": "Settings",
"SearchSettings": {
"DefaultIndexProfileName": "Search",
"Placeholder": "Search...",
"PageTitle": "Search"
}
}
]
}
npx skills add CrestApps/orchardcore-search-indexing下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer