- CMS
- 后端
- Web框架
【免费下载链接】OrchardCore
Orchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.
在 Orchard Core 中,"搜索"并不是某一个单一模块,而是一套由索引基础设施(Indexing)、索引提供程序(Lucene / Elasticsearch / Azure AI Search)、**前端搜索模块(Search)与查询管理模块(Queries)**协同构成的完整技术栈。本篇指南以官方 Search 主题文档为骨架,结合仓库源码与实际配置,系统讲解"如何定义要索引的信息、如何查询这些信息、如何为站点提供集成式搜索体验",读完即可在真实站点上完成从启用功能、创建索引到上线搜索页的完整落地。
1. Search 主题总览:五大能力模块
src/docs/topics/search/README.md是官方对"搜索"这一主题的入口文档,它明确了两件事:
- 索引(Indexing 词汇表中的定义):先决定哪些数据需要被索引;
- 查询(Query):再决定如何查询这些索引,从而提供"集成的搜索体验(integrated Search experience)"。
围绕这一目标,官方将内容组织为五个子主题,本文即沿此骨架逐一展开:
| 子主题 | 定位 | 对应文档 |
|---|---|---|
| Indexing | 通用索引基础设施,追加式任务日志 | 索引的核心机制 |
| SQL Indexing | 基于数据库表的 SQL 索引与查询 | 轻量级查询路径 |
| Lucene | 基于 Lucene.NET 的全文索引提供程序 | 全文检索主力实现 |
| Queries | 查询的管理 UI 与 Web API | 统一的查询入口 |
| Full text search implementation | 7 步实战指南 | 从零搭建站点全文搜索 |
2. 索引基础设施:OrchardCore.Indexing
2.1 追加式任务日志与游标式接口
索引模块(对应文档 Indexing 参考)是整个搜索体系的地基。它维护了一个只追加(append-only)的索引任务日志,每条任务代表一次Update(更新)或Deletion(删除),按时间顺序形成变更记录,并通过**游标式接口(cursor-based interface)**对外暴露。这样的设计使消费方可以:
- 按自己的节奏处理变更(不会阻塞内容编辑);
- 实现自定义搜索管道、分析、审计或事件驱动工作流;
- 与外部系统同步、重建索引或响应特定数据事件。
从源码结构看,这套设计实现了"索引基础设施"与"内容消费方"之间的松散耦合:内容项索引只是核心Indexing基础设施的一个消费方(Content类别),因此索引系统并不局限于内容项——用户记录、产品、外部 API 数据等任何文档都可以被索引。
2.2 统一索引管理 UI 与内容项索引
从 Orchard Core 3.0 开始,索引模块在管理后台提供了统一界面:Search > Indexes,支持:
- 创建与配置索引配置文件(Index Profile);
- 重置(Reset)或重建(Rebuild)已有索引;
- 查看各提供程序专属选项;
- 配置要索引的数据类型(例如内容类型)。
启用内容项索引后,系统通过追加式任务日志跟踪内容项变更,其他模块可用各自的游标位置消费该日志,实现"同步、分析、审计"等自定义管道。
2.3 索引自定义数据源
若要索引非内容数据,可实现以下三个接口(源码位置:src/OrchardCore.Modules/OrchardCore.Indexing及OrchardCore.Indexing.Core):
IIndexManager:控制索引任务如何被管理;IIndexDocumentManager:将实体转换为可索引文档;IIndexNameProvider:为索引配置文件提供名称。
然后在Startup.cs中注册:
services.AddIndexingSource<CustomSourceIndexManager, CustomSourceDocumentIndexManager, CustomSourceIndexNameProvider>( "ProviderName", // 例如 "Lucene"、"Elasticsearch"、"AzureAISearch" "CustomCategory", // 唯一的源类别名 o => { o.DisplayName = S["Custom Source in Provider"]; o.Description = S["Creates an index for a custom data source using the selected provider."]; });如需后台 UI 集成,可继承DisplayDriver<IndexProfile>提供配置界面,或实现IIndexProfileHandler响应索引的创建、更新、删除生命周期事件。
2.4 索引相关的 Recipe 步骤
索引配置文件可以在 Recipe 执行期间创建。官方推荐使用CreateOrUpdateIndexProfile步骤(对应源码src/OrchardCore.Indexing.Core中的 Recipe 执行器):
{ "steps":[ { "name":"CreateOrUpdateIndexProfile", "indexes": [ { "Id": "The id", "Name": "UniqueName", "IndexName": "blogposts", "ProviderName": "ProviderName", "Type": "Content", "Properties": { "ContentIndexMetadata": { "IndexLatest": false, "IndexedContentTypes": ["BlogPosts"], "Culture": "any" } } } ] } ] }注意:要索引内容项,请使用内置的Content类别,以保证与内容索引 UI 和配置体验完全兼容。
**重置(Reset)与重建(Rebuild)**是两种常见的生命周期操作,二者都在后台异步执行、不阻塞其他操作:
ResetIndex:从头重启索引过程以更新当前内容项,保留已有条目,只补充新增/更新的内容;RebuildIndex:删除并重建整个索引,从零开始填充。
{ "steps":[ { "name":"ResetIndex", "indexNames":["IndexName1","IndexName2"] } ] }若要作用于所有索引,改用"IncludeAll": true;RebuildIndex步骤结构与ResetIndex完全一致,仅步骤名不同。
3. 前端搜索模块:OrchardCore.Search
3.1 模块职责边界
OrchardCore.Search模块(对应文档 Search 参考)只提供面向用户的搜索 UI(搜索页 + 搜索表单),索引本身由Indexing基础设施配合提供程序(Lucene、Elasticsearch 或 Azure AI Search)创建与管理。因此使用前提是:启用OrchardCore.Search及至少一个提供程序功能,并创建一个索引配置文件。
3.2 搜索路由与参数
模块注册了搜索端点(源码见 SearchController.cs):
/search/{index?}index(可选):要查询的索引配置文件名称;省略时使用搜索设置中的默认索引;terms:搜索词,通过查询字符串传递,例如/search?terms=orchard。
边界行为:未指定索引且未配置默认索引时,页面显示警告提示(No default search index has been configured.);请求的索引不存在时返回404。结果按站点配置的页面大小分页,并自动生成"上一页 / 下一页"链接。
从 SearchController.cs 的源码可以确认分页实现:查询时会多取一条(PageSize + 1)用于判断是否还有更多结果,并通过PagerSlim的Before/After参数计算游标;命中后先由提供程序返回ContentItemIds,再通过ContentItemIndex按Published(或Latest,取决于索引是否包含草稿)回查数据库,最后按搜索结果的原始顺序重新排序展示。
3.3 站点搜索设置
在后台Search > Settings > Site Search(需要Manage Search Settings权限)中可配置三项设置,对应源码 SearchSettingsViewModel.cs:
| 设置项 | 说明 |
|---|---|
DefaultIndexProfileName | URL 中未指定索引时默认查询的索引配置文件 |
PageTitle | 搜索结果页显示的标题 |
Placeholder | 搜索输入框中的占位提示文本(支持数据本地化) |
这些设置可通过Search Settings 部署步骤(SearchSettingsDeploymentStep,源码位于 Deployment 目录)随部署计划导出与导入。
3.4 权限模型
| 权限 | 说明 |
|---|---|
Manage Search Settings | 允许配置站点搜索设置 |
Query Search Index | 允许查询搜索索引,按索引配置文件单独授权 |
两项权限默认授予Administrator角色。要让匿名用户或其他角色使用搜索页,需为对应索引配置文件授予Query Search Index权限(在角色编辑页的OrchardCore.Lucene Feature等分区中可见)。
3.5 搜索表单与主题定制
模块内置SearchFormPart与Search Form部件,可将搜索框放在任意位置(如某个 Layer 区域或页面),表单以GET提交到/search端点并携带Terms参数。
搜索 UI 全部通过 Shape 渲染,可在主题中覆盖模板:
| Shape | 模板 | 用途 |
|---|---|---|
Search | Search.cshtml | 整体搜索页 |
Search-Form | Search-Form.cshtml | 搜索输入表单 |
Search-Results | Search-Results.cshtml | 结果列表 |
Search-List | Search-List.cshtml | 结果项容器 |
当底层提供程序返回高亮信息时,结果页还会呈现高亮标记。
4. 全文本搜索实施指南(7 步实战)
src/docs/guides/implement-fulltext-search/README.md提供了一份面向真实站点的完整落地步骤。以下步骤以Lucene为例编写,同样的目标用Elasticsearch也能实现;此外TheBlogTheme内置的 Recipe 会自动完成全部配置,无需手工操作。
第 1 步:启用 Lucene 或 Elasticsearch 功能
Orchard Core 中Lucene与Elasticsearch各有若干功能项。要创建Lucene索引需启用Lucene功能;要创建Elasticsearch索引需启用Elasticsearch功能。
第 2 步:创建索引
点击"Add Index"打开创建表单,主要选项如下:
- Index Name(索引名称):用于标识索引。创建后会在
/App_Data/Sites/{YourTenantName}/Lucene/{IndexName}生成目录,存放 Lucene 索引过程产生的全部文件。 - Analyzer Name(分析器名称):面向高级用户,用于微调文本在索引时的词干化(stemming)处理。例如搜索 "Car" 时也希望命中小写的 "car",可配置带小写过滤器的分析器。Orchard Core 默认仅提供
standardanalyzer(针对英文文化字符优化),分析器可扩展——既可使用 Lucene.NET 自带的分析器,也可自行实现并注册。 - Culture(文化):默认
Any culture,可限定索引只处理特定文化的内容项。 - Content Types(内容类型):选择本索引要解析的内容类型。
- Index latest version(索引最新版本):勾选后除已发布项外还会索引草稿(适合自定义前端仪表板或后台模块内的搜索);默认不勾选,只索引已发布内容项。
- Store source data(仅 Elasticsearch):默认开启,控制是否在 Elasticsearch 的
_source字段中存储原始数据。
注册自定义分析器的官方示例(在自定义模块的Startup.cs中通过 DI 注册):
using Microsoft.Extensions.DependencyInjection; using OrchardCore.Lucene.Model; using OrchardCore.Lucene.Services; using OrchardCore.Modules; namespace OrchardCore.Lucene.FrenchAnalyzer { [Feature("OrchardCore.Lucene.FrenchAnalyzer")] public sealed class Startup : StartupBase { public override void ConfigureServices(IServiceCollection services) { services.Configure<LuceneOptions>(o => o.Analyzers.Add(new LuceneAnalyzer("frenchanalyzer", new MyAnalyzers.FrenchAnalyzer(LuceneSettings.DefaultVersion)))); } } }这与仓库中的实现完全吻合:Lucene 模块的 Startup.cs 正是通过services.Configure<LuceneOptions>(o => o.Analyzers.Add(new LuceneAnalyzer(LuceneConstants.DefaultAnalyzer, new StandardAnalyzer(LuceneConstants.DefaultVersion))))注册默认的standardanalyzer。
第 3 步:配置搜索设置
启用Lucene模块后,站点会新增/search路由映射,需要相应设置才能工作。创建索引后应第一时间到后台配置:指定/search页面使用的索引,以及该搜索页查询的索引字段——通常默认使用Content.ContentItem.FullText。
第 4 步:设置索引权限
默认情况下每个索引都有权限保护,未显式放开的索引任何人都无法查询。要让 "Search" 索引对站点Anonymous(匿名)用户开放,需编辑该角色并在OrchardCore.Lucene Feature分区中为对应索引勾选查询权限——每个索引都会在此列出。
第 5 步:设置搜索提供程序
从 Orchard Core 1.5 起,可通过Search功能启用站点前端搜索:启用后会新增后台菜单项,用于选择前端搜索使用的索引提供程序(Lucene或Elasticsearch)。
第 6 步:测试搜索页
以TheBlogTheme的 Recipe 为例,其已自动完成全部配置,搜索页可直接返回结果:
第 7 步:细调全文本搜索
每个内容类型定义中都有"该内容项哪些部分应作为FullText被索引"的配置区。默认索引"显示文本(display text)"与"正文部件(body part)",但可通过"Use custom full-text"选项自定义要索引的内容,填写任意 Liquid 脚本。例如追加索引副标题字段:
{{ Model.Content.BlogPost.Subtitle.Text }}利用自定义 FullText 还可以把 Widget 或 Bag 的内容纳入全文索引,例如 FlowPart 内的 Widget:
{% for contentItem in Model.Content.FlowPart.Widgets %} {{ contentItem | full_text }} {% endfor %}或简写为:
{{ Model.Content.FlowPart.Widgets | full_text }}可选:搜索模板定制
可在主题中覆盖以下模板文件:
/Views/Shared/Search.liquid或.cshtml(整体布局)/Views/Search-Form.liquid或.cshtml(表单布局)/Views/Search-Results.liquid或.cshtml(结果布局)
例如把结果模板中的Summary换成SearchSummary并创建对应 Shape 模板:
{% if Model.ContentItems != null and Model.ContentItems.size > 0 %} <ul class="list-group"> {% for item in Model.ContentItems %} <li class="list-group-item"> {{ item | shape_build_display: "SearchSummary" | shape_render }} </li> {% endfor %} </ul> {% elsif Model.Terms != null %} <p class="alert alert-warning">{{"There are no such results." | t }}</p> {% endif %}5. 查询层:OrchardCore.Queries与搜索 API
5.1 Queries 模块
查询模块(对应文档 Queries 参考)为查询数据提供管理 UI 与 API,并支持自定义查询源(Query Source):
- 创建继承自
Query的类,表示新查询所需的状态; - 创建实现
IQuerySource的类以暴露新查询类型,例如:
services.AddScoped<IQuerySource, LuceneQuerySource>();- 通过继承
DisplayDriver<Query, LuceneQuery>提供查询编辑器; - 查询类型列表界面使用 Shape
Query_Link__[QuerySource],例如源为Lucene时对应模板Query-Lucene.Link.cshtml。
Recipe 中可用queries步骤创建查询:
{ "name": "queries", "Queries": [ { "Name": "AwesomeQuery", "Source": "Lucene", // 具体查询类型的属性 ... }] }5.2 Web API
Queries 模块暴露通用查询端点api/queries/{name},支持POST与GET:
| 参数 | 示例 | 说明 |
|---|---|---|
name | myQuery | 要执行的查询名称 |
parameters | { size: 3 } | 查询参数的 JSON 对象 |
Lucene 模块另提供两个专用端点(对应文档 Lucene 参考 的 Web APIs 章节):
api/lucene/content:执行指定名称的查询并返回对应内容项;api/lucene/documents:执行指定名称的查询并返回 Lucene 文档(仅返回已存储字段)。
两者均支持POST/GET,参数一致:
| 参数 | 示例 | 说明 |
|---|---|---|
indexName | search | 要查询的索引名称 |
query | { "query": { "match_all": {} }, "size": 10 } | 表示查询的 JSON 对象 |
parameters | { size: 3 } | 查询参数 JSON 对象 |
Lucene 查询使用Elasticsearch Query DSL语法编写;若要通过 GraphQL 暴露查询,需定义返回类型 schema——返回ContentItem(如BlogPost)时勾选Return Content Items并配置{ "type": "ContentItem/BlogPost" }。
5.3 Lucene 的 Recipe 与索引同步
旧版lucene-index/LuceneIndexSettings步骤仍可创建索引,但官方已标注弃用,推荐改用CreateOrUpdateIndexProfile:
{ "steps":[ { "name":"CreateOrUpdateIndexProfile", "indexes": [ { "Name": "BlogPostsLucene", "IndexName": "blogposts", "ProviderName": "Lucene", "Type": "Content", "Properties": { "ContentIndexMetadata": { "IndexLatest": false, "IndexedContentTypes": ["BlogPosts"], "Culture": "any" }, "LuceneIndexMetadata": { "AnalyzerName": "standard", "StoreSourceData": true } } } ] } ] }此外,OrchardCore.Search.Lucene.Worker是一个旧版兼容功能:它创建后台任务,让本机文件系统索引与可能各自持有本地索引的其他实例保持同步。官方建议仅在同一租户运行于多实例(farm)且使用 Lucene 文件系统索引时启用;若运行在 Azure App Services 或使用 Elasticsearch 则无需此功能。
6. SQL 索引:OrchardCore.SQLIndexing与字段级索引表
如果不依赖外部全文搜索引擎,还可以直接查询数据库索引表(对应文档 SQL Indexing 参考)。
6.1 内容项索引表
ContentItemIndex是查询内容项的核心表:
| 列名 | 类型 | 非空 | 主键 |
|---|---|---|---|
Id | int | true | true |
DocumentId | int | false | false |
ContentItemId | nvarchar(26) | false | false |
Published | bit | false | false |
Latest | bit | false | false |
ModifiedUtc | datetime | false | false |
PublishedUtc | datetime | false | false |
CreatedUtc | datetime | false | false |
Owner | nvarchar(255) | false | false |
Author | nvarchar(255) | false | false |
DisplayText | nvarchar(255) | false | false |
多语言环境下另有LocalizedContentItemIndex,在基础列之上追加LocalizationSet(nvarchar)与Culture(nvarchar)两列。
6.2 内容字段索引表
OrchardCore.ContentFields.Indexing.SQL模块为内容字段提供数据库索引。以BooleanFieldIndex为例,它在公共列(DocumentId、ContentItemId、ContentItemVersionId、ContentType、ContentPart、ContentField、Published、Latest)之外附加字段值列Boolean(bit)。ContentPickerFieldIndex则提供SelectedContentItemId(nvarchar(26))列,DateFieldIndex提供日期列。注意:表中列出的类型为SQL Server 数据类型(SQLite 的文本字段无长度限制)。这些索引表可直接用 SQL 查询,是轻量级、无外部搜索引擎依赖的检索路径。
7. 串联全链路:从内容项到搜索结果
结合以上模块与源码,一次搜索请求的完整链路可归纳为:
- 索引写入:内容项保存时,
OrchardCore.Indexing的追加式任务日志记录Update/Deletion;Lucene / Elasticsearch 等提供程序按游标消费日志,把内容项转换后的文档写入索引(Lucene 落盘到App_Data/Sites/{Tenant}/Lucene/{IndexName},Elasticsearch 写入远端集群)。 - 搜索请求:用户提交
GET /search?terms=...,SearchController.cs 解析 URL 中的索引名或回退到默认索引,校验Query Search Index权限,再通过ISearchService(按提供程序名从 DI 取键控服务,如LuceneSearchService)执行查询。 - 结果组装:提供程序返回按相关性排序的
ContentItemIds与高亮信息;控制器按Published/Latest回查ContentItemIndex,按搜索顺序重排,用PagerSlim生成分页链接,最终渲染Search/Search-Results等 Shape 模板。
8. 总结与选型建议
| 路径 | 适用场景 | 核心文档 |
|---|---|---|
| Lucene(本地文件索引) | 单实例站点、开箱即用的全文检索、无外部依赖 | Lucene |
| Elasticsearch | 多实例、大规模内容、需要集群与高可用 | Elasticsearch |
| Azure AI Search | 依托 Azure 云服务的托管搜索 | AzureAISearch |
| SQL 索引查询 | 轻量查询、字段级过滤、无需全文引擎 | SQL Indexing |
无论选择哪种提供程序,思考路径都是一致的:先用Indexing定义"索引什么",再用提供程序决定"存到哪、怎么分词",最后用Search模块与Queries模块决定"怎么查、怎么展示"。对于绝大多数内容站点,直接参照第 4 节的七步指南(或直接使用TheBlogTheme的 Recipe)即可在最短时间内获得可用的全文本搜索体验。
- CMS
- 后端
- Web框架
【免费下载链接】OrchardCore
Orchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.
相关推荐
RestKit Core Data 全文搜索:RKSearchIndexer 索引构建、RKSearchPredicate 查询与性能优化实战指南
RestKit Core Data 全文搜索:RKSearchIndexer 索引构建、RKSearchPredicate 查询与性能优化实战指南 RestKi
移动开发网络gin-vue-admin搜索引擎:全文搜索与模糊查询
gin vue admin搜索引擎:全文搜索与模糊查询 引言 在现代化的后台管理系统中,高效的数据检索功能是提升用户体验的关键。gin vue admin作为一
后端前端认证鉴权低代码任务调度Apache SeaTunnel Web UI:打开浏览器,5分钟看清数据同步作业在干什么 完整指南
Apache SeaTunnel Web UI:打开浏览器,5分钟看清数据同步作业在干什么 完整指南 上次一个千万行的同步作业跑到一半卡住,我翻了一下午日志才定
数据集成ETL大数据批处理流处理变更数据捕获
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考