mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-21 03:14:10 +08:00
Incremental work on multiple indexes
--HG-- branch : 1.x extra : rebase_source : 39da1f112307ff02bf7a01ecabc95a43271d476c
This commit is contained in:
@@ -111,7 +111,7 @@ namespace Orchard.Tests.Modules.Indexing {
|
||||
|
||||
var thingType = new ContentTypeDefinitionBuilder()
|
||||
.Named(ThingDriver.ContentTypeName)
|
||||
.WithSetting("TypeIndexing.Included", "true")
|
||||
.WithSetting("TypeIndexing.Indexes", "Search")
|
||||
.Build();
|
||||
|
||||
_contentDefinitionManager
|
||||
@@ -145,7 +145,7 @@ namespace Orchard.Tests.Modules.Indexing {
|
||||
public void ShouldNotIndexContentIfIndexDocumentIsEmpty() {
|
||||
var alphaType = new ContentTypeDefinitionBuilder()
|
||||
.Named("alpha")
|
||||
.WithSetting("TypeIndexing.Included", "true") // the content types should be indexed, but there is no content at all
|
||||
.WithSetting("TypeIndexing.Indexes", "Search") // the content types should be indexed, but there is no content at all
|
||||
.Build();
|
||||
|
||||
_contentDefinitionManager
|
||||
|
@@ -6,4 +6,5 @@ Version: 1.6
|
||||
OrchardVersion: 1.4.1
|
||||
Description: The Lucene module enables the site to be indexed using Lucene.NET. The index generated by this module can then be used by the search module to provide an integrated full-text search experience to a web site.
|
||||
FeatureDescription: Lucene indexing services.
|
||||
Dependencies: Orchard.Indexing
|
||||
Category: Search
|
||||
|
@@ -2,9 +2,9 @@
|
||||
using System.Linq;
|
||||
using Orchard.Commands;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Indexing.Services;
|
||||
using Orchard.Tasks.Indexing;
|
||||
using Orchard.Utility.Extensions;
|
||||
|
||||
namespace Orchard.Indexing.Commands {
|
||||
public class IndexingCommands : DefaultOrchardCommandHandler {
|
||||
@@ -12,7 +12,6 @@ namespace Orchard.Indexing.Commands {
|
||||
private readonly IIndexingService _indexingService;
|
||||
private readonly IIndexingTaskManager _indexingTaskManager;
|
||||
private readonly IContentManager _contentManager;
|
||||
private const string SearchIndexName = "Search";
|
||||
|
||||
public IndexingCommands(
|
||||
IIndexManager indexManager,
|
||||
@@ -31,29 +30,66 @@ namespace Orchard.Indexing.Commands {
|
||||
[OrchardSwitch]
|
||||
public string ContentItem { get; set; }
|
||||
|
||||
[CommandName("index create")]
|
||||
[CommandHelp("index create <index>\r\n\t" + "Creates a new index with the specified name")]
|
||||
public void Create(string index) {
|
||||
if (!_indexManager.HasIndexProvider()) {
|
||||
Context.Output.WriteLine(T("No index service available"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(index)) {
|
||||
Context.Output.WriteLine(T("Invalid index name."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (index.ToSafeName() != index) {
|
||||
Context.Output.WriteLine(T("Invalid index name."));
|
||||
}
|
||||
else {
|
||||
_indexManager.GetSearchIndexProvider().CreateIndex(index);
|
||||
Context.Output.WriteLine(T("New index has been created succeffully."));
|
||||
}
|
||||
}
|
||||
|
||||
[CommandName("index update")]
|
||||
[CommandHelp("index update\r\n\t" + "Updates the search index")]
|
||||
public void Update() {
|
||||
_indexingService.UpdateIndex(SearchIndexName);
|
||||
[CommandHelp("index update <index>\r\n\t" + "Updates the specified index")]
|
||||
public void Update(string index) {
|
||||
if (string.IsNullOrWhiteSpace(index)) {
|
||||
Context.Output.WriteLine(T("Invalid index name."));
|
||||
return;
|
||||
}
|
||||
|
||||
_indexingService.UpdateIndex(index);
|
||||
Context.Output.WriteLine(T("Index is now being updated..."));
|
||||
}
|
||||
|
||||
[CommandName("index rebuild")]
|
||||
[CommandHelp("index rebuild \r\n\t" + "Rebuilds the search index")]
|
||||
public void Rebuild() {
|
||||
_indexingService.RebuildIndex(SearchIndexName);
|
||||
[CommandHelp("index rebuild <index> \r\n\t" + "Rebuilds the specified index")]
|
||||
public void Rebuild(string index) {
|
||||
if (string.IsNullOrWhiteSpace(index)) {
|
||||
Context.Output.WriteLine(T("Invalid index name."));
|
||||
return;
|
||||
}
|
||||
|
||||
_indexingService.RebuildIndex(index);
|
||||
Context.Output.WriteLine(T("Index is now being rebuilt..."));
|
||||
}
|
||||
|
||||
[CommandName("index search")]
|
||||
[CommandHelp("index search /Query:<query>\r\n\t" + "Searches the specified <query> terms in the search index")]
|
||||
[CommandName("index query")]
|
||||
[CommandHelp("index query <index> /Query:<query>\r\n\t" + "Searches the specified <query> terms in the specified index")]
|
||||
[OrchardSwitches("Query")]
|
||||
public void Search() {
|
||||
public void Search(string index) {
|
||||
if (string.IsNullOrWhiteSpace(index)) {
|
||||
Context.Output.WriteLine(T("Invalid index name."));
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !_indexManager.HasIndexProvider() ) {
|
||||
Context.Output.WriteLine(T("No index available"));
|
||||
return;
|
||||
}
|
||||
var searchBuilder = _indexManager.GetSearchIndexProvider().CreateSearchBuilder(SearchIndexName);
|
||||
var searchBuilder = _indexManager.GetSearchIndexProvider().CreateSearchBuilder(index);
|
||||
var results = searchBuilder.Parse( new [] {"body", "title"}, Query).Search();
|
||||
|
||||
Context.Output.WriteLine("{0} result{1}\r\n-----------------\r\n", results.Count(), results.Count() > 0 ? "s" : "");
|
||||
@@ -76,15 +112,20 @@ namespace Orchard.Indexing.Commands {
|
||||
}
|
||||
|
||||
[CommandName("index stats")]
|
||||
[CommandHelp("index stats\r\n\t" + "Displays some statistics about the search index")]
|
||||
[CommandHelp("index stats <index>\r\n\t" + "Displays some statistics about the search index")]
|
||||
[OrchardSwitches("IndexName")]
|
||||
public void Stats() {
|
||||
public void Stats(string index) {
|
||||
if (string.IsNullOrWhiteSpace(index)) {
|
||||
Context.Output.WriteLine(T("Invalid index name."));
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !_indexManager.HasIndexProvider() ) {
|
||||
Context.Output.WriteLine(T("No index available"));
|
||||
return;
|
||||
}
|
||||
|
||||
Context.Output.WriteLine(T("Number of indexed documents: {0}", _indexManager.GetSearchIndexProvider().NumDocs(SearchIndexName)));
|
||||
Context.Output.WriteLine(T("Number of indexed documents: {0}", _indexManager.GetSearchIndexProvider().NumDocs(index)));
|
||||
}
|
||||
|
||||
[CommandName("index refresh")]
|
||||
|
@@ -15,5 +15,8 @@ namespace Orchard.Indexing {
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// todo: When upgrading from 1, change TypeIndexing.Included to TypeIndexing.Indexes = "Search"
|
||||
// todo: When upgrading from 1, define SearchSettingsPart.SelectedIndex to "Search"
|
||||
}
|
||||
}
|
@@ -68,7 +68,7 @@ namespace Orchard.Indexing.Services
|
||||
handler.UpdateIndex(indexName);
|
||||
}
|
||||
|
||||
Services.Notifier.Information(T("The search index has been updated."));
|
||||
Services.Notifier.Information(T("The index {0} has been updated.", indexName));
|
||||
}
|
||||
|
||||
IndexEntry IIndexingService.GetIndexEntry(string indexName) {
|
||||
|
@@ -166,6 +166,13 @@ namespace Orchard.Indexing.Services {
|
||||
|
||||
foreach (var item in contentItems) {
|
||||
try {
|
||||
|
||||
// skip items from types which are not indexed
|
||||
var settings = GetTypeIndexingSettings(item);
|
||||
if (!settings.Indexes.Contains(indexName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IDocumentIndex documentIndex = ExtractDocumentIndex(item);
|
||||
|
||||
if (documentIndex != null && documentIndex.IsDirty) {
|
||||
@@ -195,8 +202,17 @@ namespace Orchard.Indexing.Services {
|
||||
|
||||
foreach (var item in indexingTasks) {
|
||||
try {
|
||||
|
||||
IDocumentIndex documentIndex = null;
|
||||
|
||||
// item.ContentItem can be null if the content item has been deleted
|
||||
IDocumentIndex documentIndex = ExtractDocumentIndex(item.ContentItem);
|
||||
if (item.ContentItem != null) {
|
||||
// skip items from types which are not indexed
|
||||
var settings = GetTypeIndexingSettings(item.ContentItem);
|
||||
if (!settings.Indexes.Contains(indexName)) {
|
||||
documentIndex = ExtractDocumentIndex(item.ContentItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (documentIndex == null || item.Delete) {
|
||||
deleteFromIndex.Add(item.Id);
|
||||
@@ -284,12 +300,6 @@ namespace Orchard.Indexing.Services {
|
||||
return null;
|
||||
}
|
||||
|
||||
// skip items from types which are not indexed
|
||||
var settings = GetTypeIndexingSettings(contentItem);
|
||||
if (!settings.Included) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var documentIndex = _indexProvider.New(contentItem.Id);
|
||||
|
||||
// call all handlers to add content to index
|
||||
@@ -301,7 +311,7 @@ namespace Orchard.Indexing.Services {
|
||||
if (contentItem == null ||
|
||||
contentItem.TypeDefinition == null ||
|
||||
contentItem.TypeDefinition.Settings == null) {
|
||||
return new TypeIndexing {Included = false};
|
||||
return new TypeIndexing {Indexes = ""};
|
||||
}
|
||||
return contentItem.TypeDefinition.Settings.GetModel<TypeIndexing>();
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.ContentManagement.MetaData.Builders;
|
||||
@@ -28,7 +29,7 @@ namespace Orchard.Indexing.Settings {
|
||||
public override IEnumerable<TemplateViewModel> TypeEditorUpdate(ContentTypeDefinitionBuilder builder, IUpdateModel updateModel) {
|
||||
var model = new TypeIndexing();
|
||||
updateModel.TryUpdateModel(model, "TypeIndexing", null, null);
|
||||
builder.WithSetting("TypeIndexing.Included", model.Included ? true.ToString() : null);
|
||||
builder.WithSetting("TypeIndexing.Indexes", model.Indexes);
|
||||
|
||||
CreateIndexingTasks();
|
||||
|
||||
|
@@ -1,5 +1,12 @@
|
||||
namespace Orchard.Indexing.Settings {
|
||||
using System;
|
||||
|
||||
namespace Orchard.Indexing.Settings {
|
||||
public class TypeIndexing {
|
||||
public bool Included { get; set; }
|
||||
public string Indexes { get; set; }
|
||||
|
||||
public string[] List {
|
||||
get { return String.IsNullOrEmpty(Indexes) ? new string[0] : Indexes.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); }
|
||||
set { Indexes = String.Join(",", value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -61,7 +61,7 @@
|
||||
}
|
||||
} else
|
||||
{
|
||||
<div>@T("There are no indexes.")</div>
|
||||
<div>@T("There is currently no index.")</div>
|
||||
}
|
||||
|
||||
@if (Model.IndexProvider != null) {
|
||||
|
@@ -1,7 +1,21 @@
|
||||
@model Orchard.Indexing.Settings.TypeIndexing
|
||||
@using Orchard.Indexing
|
||||
@using Orchard.Utility.Extensions
|
||||
@model Orchard.Indexing.Settings.TypeIndexing
|
||||
@{
|
||||
var indexProvider = WorkContext.Resolve<IIndexManager>().GetSearchIndexProvider();
|
||||
|
||||
}
|
||||
|
||||
<fieldset>
|
||||
@Html.EditorFor(m=>m.Included)
|
||||
<label for="@Html.FieldIdFor(m => m.Included)" class="forcheckbox">@T("Index this content type for search")</label>
|
||||
@Html.ValidationMessageFor(m=>m.Included)
|
||||
</fieldset>
|
||||
@if (indexProvider != null) {
|
||||
<fieldset>
|
||||
<legend>@T("Index this content type in:")</legend>
|
||||
@{ var i = 0;}
|
||||
@foreach (var index in indexProvider.List()) {
|
||||
<div>
|
||||
<input type="checkbox" value="@index" class="check-box" id="@Html.FieldIdFor(m => m.List[i])" name="@Html.FieldNameFor(m => m.List)" checked="@(Model.List.Contains(index))"/>
|
||||
<label for="@Html.FieldIdFor(m => m.List[i])" class="forcheckbox">@index</label>
|
||||
</div>
|
||||
i++;
|
||||
}
|
||||
</fieldset>
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
using Orchard.Commands;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Search.Models;
|
||||
|
||||
namespace Orchard.Search.Commands {
|
||||
public class SearchCommands : DefaultOrchardCommandHandler {
|
||||
private readonly IOrchardServices _orchardServices;
|
||||
|
||||
public SearchCommands(IOrchardServices orchardServices) {
|
||||
_orchardServices = orchardServices;
|
||||
}
|
||||
|
||||
[CommandName("search use")]
|
||||
[CommandHelp("search use <index>\r\n\t" + "Defines the default index to use for search")]
|
||||
public void Index(string index) {
|
||||
var settings = _orchardServices.WorkContext.CurrentSite.As<SearchSettingsPart>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(index)) {
|
||||
Context.Output.WriteLine(T("Invalid index name."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings != null) {
|
||||
settings.SearchIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -36,15 +36,16 @@ namespace Orchard.Search.Controllers {
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ActionResult Index(PagerParameters pagerParameters, string searchText = "") {
|
||||
Pager pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
|
||||
var searchFields = Services.WorkContext.CurrentSite.As<SearchSettingsPart>().SearchedFields;
|
||||
|
||||
var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
|
||||
var searchSettingsPart = Services.WorkContext.CurrentSite.As<SearchSettingsPart>();
|
||||
|
||||
IPageOfItems<ISearchHit> searchHits = new PageOfItems<ISearchHit>(new ISearchHit[] { });
|
||||
try {
|
||||
|
||||
searchHits = _searchService.Query(searchText, pager.Page, pager.PageSize,
|
||||
Services.WorkContext.CurrentSite.As<SearchSettingsPart>().Record.FilterCulture,
|
||||
searchFields,
|
||||
searchSettingsPart.SearchIndex,
|
||||
searchSettingsPart.SearchedFields,
|
||||
searchHit => searchHit);
|
||||
}
|
||||
catch (Exception exception) {
|
||||
|
@@ -64,7 +64,8 @@ namespace Orchard.Search.Controllers {
|
||||
return View("NoIndex");
|
||||
}
|
||||
|
||||
var builder = _indexManager.GetSearchIndexProvider().CreateSearchBuilder("Search");
|
||||
var builder = _indexManager.GetSearchIndexProvider().CreateSearchBuilder(settings.SearchIndex);
|
||||
|
||||
try {
|
||||
builder.Parse(searchFields, searchText);
|
||||
|
||||
|
@@ -46,14 +46,20 @@ namespace Orchard.Search.Controllers {
|
||||
|
||||
public ActionResult Index(PagerParameters pagerParameters, string q = "") {
|
||||
var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
|
||||
var searchFields = Services.WorkContext.CurrentSite.As<SearchSettingsPart>().SearchedFields;
|
||||
var searchSettingPart = Services.WorkContext.CurrentSite.As<SearchSettingsPart>();
|
||||
|
||||
if (String.IsNullOrEmpty(searchSettingPart.SearchIndex)) {
|
||||
Services.Notifier.Error(T("Please define a default search index"));
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
IPageOfItems<ISearchHit> searchHits = new PageOfItems<ISearchHit>(new ISearchHit[] { });
|
||||
try {
|
||||
|
||||
searchHits = _searchService.Query(q, pager.Page, pager.PageSize,
|
||||
Services.WorkContext.CurrentSite.As<SearchSettingsPart>().Record.FilterCulture,
|
||||
searchFields,
|
||||
searchSettingPart.SearchIndex,
|
||||
searchSettingPart.SearchedFields,
|
||||
searchHit => searchHit);
|
||||
} catch(Exception exception) {
|
||||
Logger.Error(T("Invalid search query: {0}", exception.Message).Text);
|
||||
|
@@ -11,7 +11,6 @@ using Orchard.Search.ViewModels;
|
||||
namespace Orchard.Search.Drivers {
|
||||
|
||||
public class SearchSettingsPartDriver : ContentPartDriver<SearchSettingsPart> {
|
||||
private const string SearchIndexName = "Search";
|
||||
private readonly IIndexManager _indexManager;
|
||||
|
||||
public SearchSettingsPartDriver(IIndexManager indexManager) {
|
||||
@@ -30,24 +29,33 @@ namespace Orchard.Search.Drivers {
|
||||
|
||||
protected override DriverResult Editor(SearchSettingsPart part, IUpdateModel updater, dynamic shapeHelper) {
|
||||
return ContentShape("Parts_Search_SiteSettings", () => {
|
||||
SearchSettingsViewModel model = new SearchSettingsViewModel();
|
||||
var model = new SearchSettingsViewModel();
|
||||
String[] searchedFields = part.SearchedFields;
|
||||
|
||||
if (updater != null) {
|
||||
// submitting: rebuild model from form data
|
||||
if (updater.TryUpdateModel(model, Prefix, null, null)) {
|
||||
// update part if successful
|
||||
part.SearchedFields = model.Entries.Where(e => e.Selected).Select(e => e.Field).ToArray();
|
||||
part.SearchIndex = model.SelectedIndex;
|
||||
part.SearchedFields = model.Entries.First(e => e.Index == model.SelectedIndex).Fields.Where(e => e.Selected).Select(e => e.Field).ToArray();
|
||||
part.FilterCulture = model.FilterCulture;
|
||||
}
|
||||
}
|
||||
else if (_indexManager.HasIndexProvider()) {
|
||||
// viewing editor: build model from part
|
||||
model.FilterCulture = part.FilterCulture;
|
||||
model.Entries = new List<SearchSettingsEntry>();
|
||||
foreach (var field in _indexManager.GetSearchIndexProvider().GetFields(SearchIndexName)) {
|
||||
model.Entries.Add(new SearchSettingsEntry { Field = field, Selected = searchedFields.Contains(field) });
|
||||
}
|
||||
model.SelectedIndex = part.SearchIndex;
|
||||
model.Entries = _indexManager.GetSearchIndexProvider().List().Select(x => {
|
||||
var indexSettings = new IndexSettingsEntry {
|
||||
Index = x,
|
||||
Fields = new List<SearchSettingsEntry>()
|
||||
};
|
||||
foreach (var field in _indexManager.GetSearchIndexProvider().GetFields(x)) {
|
||||
indexSettings.Fields.Add(new SearchSettingsEntry {Field = field, Selected = (x == part.SearchIndex && searchedFields.Contains(field))});
|
||||
}
|
||||
|
||||
return indexSettings;
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
return shapeHelper.EditorTemplate(TemplateName: "Parts/Search.SiteSettings", Model: model, Prefix: Prefix);
|
||||
|
@@ -10,6 +10,7 @@ namespace Orchard.Search {
|
||||
.ContentPartRecord()
|
||||
.Column<bool>("FilterCulture")
|
||||
.Column<string>("SearchedFields", c => c.Unlimited())
|
||||
.Column<string>("SearchIndex")
|
||||
);
|
||||
|
||||
ContentDefinitionManager.AlterTypeDefinition("SearchForm",
|
||||
@@ -20,7 +21,15 @@ namespace Orchard.Search {
|
||||
.WithSetting("Stereotype", "Widget")
|
||||
);
|
||||
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
public int UpdateFrom1() {
|
||||
SchemaBuilder.AlterTable("SearchSettingsPartRecord", table => table
|
||||
.AddColumn<string>("SearchIndex")
|
||||
);
|
||||
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
@@ -12,5 +12,10 @@ namespace Orchard.Search.Models {
|
||||
get { return Record.FilterCulture; }
|
||||
set { Record.FilterCulture = value; }
|
||||
}
|
||||
|
||||
public string SearchIndex {
|
||||
get { return Record.SearchIndex; }
|
||||
set { Record.SearchIndex = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,12 +2,13 @@ using Orchard.ContentManagement.Records;
|
||||
|
||||
namespace Orchard.Search.Models {
|
||||
public class SearchSettingsPartRecord : ContentPartRecord {
|
||||
public virtual bool FilterCulture { get; set; }
|
||||
public virtual string SearchedFields { get; set; }
|
||||
|
||||
public SearchSettingsPartRecord() {
|
||||
FilterCulture = false;
|
||||
SearchedFields = "body, title";
|
||||
}
|
||||
|
||||
public virtual bool FilterCulture { get; set; }
|
||||
public virtual string SearchedFields { get; set; }
|
||||
public virtual string SearchIndex { get; set; }
|
||||
}
|
||||
}
|
@@ -47,6 +47,7 @@
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
@@ -56,6 +57,7 @@
|
||||
<Reference Include="System.Web" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Commands\SearchCommands.cs" />
|
||||
<Compile Include="ContentPickerNavigationProvider.cs" />
|
||||
<Compile Include="Controllers\ContentPickerController.cs" />
|
||||
<Compile Include="ContentAdminMenu.cs" />
|
||||
@@ -87,6 +89,9 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Module.txt" />
|
||||
<Content Include="Recipes\Search.recipe.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="Styles\orchard-search-admin.css" />
|
||||
<Content Include="Styles\orchard-search-search.css" />
|
||||
<Content Include="Views\Web.config" />
|
||||
|
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0"?>
|
||||
<Orchard>
|
||||
<Recipe>
|
||||
<Name>Search</Name>
|
||||
<Description>Configures a default search index and search settings using the Lucene engine.</Description>
|
||||
<Author>The Orchard Team</Author>
|
||||
<WebSite>http://orchardproject.net</WebSite>
|
||||
<Tags>developer</Tags>
|
||||
<Version>1.0</Version>
|
||||
</Recipe>
|
||||
|
||||
<Feature enable="Orchard.Search,Lucene" />
|
||||
|
||||
<Metadata>
|
||||
<Types>
|
||||
<Page TypeIndexing.Indexes="Search">
|
||||
</Page>
|
||||
<BlogPost TypeIndexing.Indexes="Search">
|
||||
</BlogPost>
|
||||
</Types>
|
||||
</Metadata>
|
||||
|
||||
<Migration features="*" />
|
||||
|
||||
<Command>
|
||||
index create Search
|
||||
index update Search
|
||||
search use Search
|
||||
</Command>
|
||||
</Orchard>
|
@@ -4,6 +4,6 @@ using Orchard.Indexing;
|
||||
|
||||
namespace Orchard.Search.Services {
|
||||
public interface ISearchService : IDependency {
|
||||
IPageOfItems<T> Query<T>(string query, int skip, int? take, bool filterCulture, string[] searchFields, Func<ISearchHit, T> shapeResult);
|
||||
IPageOfItems<T> Query<T>(string query, int skip, int? take, bool filterCulture, string index, string[] searchFields, Func<ISearchHit, T> shapeResult);
|
||||
}
|
||||
}
|
@@ -21,18 +21,18 @@ namespace Orchard.Search.Services {
|
||||
public IOrchardServices Services { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
ISearchBuilder Search() {
|
||||
ISearchBuilder Search(string index) {
|
||||
return _indexManager.HasIndexProvider()
|
||||
? _indexManager.GetSearchIndexProvider().CreateSearchBuilder("Search")
|
||||
? _indexManager.GetSearchIndexProvider().CreateSearchBuilder(index)
|
||||
: new NullSearchBuilder();
|
||||
}
|
||||
|
||||
IPageOfItems<T> ISearchService.Query<T>(string query, int page, int? pageSize, bool filterCulture, string[] searchFields, Func<ISearchHit, T> shapeResult) {
|
||||
IPageOfItems<T> ISearchService.Query<T>(string query, int page, int? pageSize, bool filterCulture, string index, string[] searchFields, Func<ISearchHit, T> shapeResult) {
|
||||
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
return new PageOfItems<T>(Enumerable.Empty<T>());
|
||||
|
||||
var searchBuilder = Search().Parse(searchFields, query);
|
||||
var searchBuilder = Search(index).Parse(searchFields, query);
|
||||
|
||||
if (filterCulture) {
|
||||
var culture = _cultureManager.GetSiteCulture();
|
||||
|
@@ -26,6 +26,7 @@ namespace Orchard.Search.Settings {
|
||||
var model = new ContentPickerSearchFieldSettings();
|
||||
if (updateModel.TryUpdateModel(model, "ContentPickerSearchFieldSettings", null, null)) {
|
||||
builder.WithSetting("ContentPickerSearchFieldSettings.ShowSearchTab", model.ShowSearchTab.ToString(CultureInfo.InvariantCulture));
|
||||
builder.WithSetting("ContentPickerSearchFieldSettings.SearchIndex", model.SearchIndex);
|
||||
builder.WithSetting("ContentPickerSearchFieldSettings.DisplayedContentTypes", model.DisplayedContentTypes);
|
||||
}
|
||||
|
||||
|
@@ -5,6 +5,7 @@
|
||||
}
|
||||
|
||||
public bool ShowSearchTab { get; set; }
|
||||
public string SearchIndex { get; set; }
|
||||
public string DisplayedContentTypes { get; set; }
|
||||
}
|
||||
}
|
||||
|
@@ -2,10 +2,16 @@
|
||||
|
||||
namespace Orchard.Search.ViewModels {
|
||||
public class SearchSettingsViewModel {
|
||||
public IList<SearchSettingsEntry> Entries { get; set; }
|
||||
public string SelectedIndex { get; set; }
|
||||
public IList<IndexSettingsEntry> Entries { get; set; }
|
||||
public bool FilterCulture { get; set; }
|
||||
}
|
||||
|
||||
public class IndexSettingsEntry {
|
||||
public string Index { get; set; }
|
||||
public IList<SearchSettingsEntry> Fields { get; set; }
|
||||
}
|
||||
|
||||
public class SearchSettingsEntry {
|
||||
public string Field { get; set; }
|
||||
public bool Selected { get; set; }
|
||||
|
@@ -1,4 +1,9 @@
|
||||
@model Orchard.Search.Settings.ContentPickerSearchFieldSettings
|
||||
@using Orchard.Indexing
|
||||
@model Orchard.Search.Settings.ContentPickerSearchFieldSettings
|
||||
@{
|
||||
var indexManager = WorkContext.Resolve<IIndexManager>();
|
||||
var indexProvider = indexManager.GetSearchIndexProvider();
|
||||
}
|
||||
|
||||
<fieldset>
|
||||
<div>
|
||||
@@ -7,6 +12,16 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<div>
|
||||
<label for="@Html.FieldIdFor(m => m.SearchIndex)">@T("Search index")</label>
|
||||
<select name="@Html.FieldNameFor(m => m.SearchIndex)" >
|
||||
@foreach (string index in indexProvider.List()) {
|
||||
@Html.SelectOption(Model.SearchIndex, index, index)
|
||||
}
|
||||
</select>
|
||||
<span class="hint">@T("The index to use when displaying this tab.")</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="@Html.FieldIdFor(m => m.DisplayedContentTypes)">@T("Content Types and Parts")</label>
|
||||
@Html.TextBoxFor(m => m.DisplayedContentTypes)
|
||||
|
@@ -1,28 +1,55 @@
|
||||
@model Orchard.Search.ViewModels.SearchSettingsViewModel
|
||||
@using Orchard.Search.ViewModels;
|
||||
|
||||
@using Orchard.Search.ViewModels
|
||||
@model Orchard.Search.ViewModels.SearchSettingsViewModel
|
||||
@{
|
||||
Script.Require("jQuery");
|
||||
|
||||
}
|
||||
<fieldset>
|
||||
<legend>@T("Search")</legend>
|
||||
<div>
|
||||
<label>@T("Searched fields")</label>
|
||||
<span class="hint">@T("Check any property which should be used for search queries.")</span>
|
||||
@{var entryIndex = 0;}
|
||||
|
||||
<label>@T("Indexes")</label>
|
||||
|
||||
@if (Model.Entries != null && Model.Entries.Any()) {
|
||||
<ul>
|
||||
@foreach(var modelEntry in Model.Entries) {
|
||||
<li>
|
||||
@Html.EditorFor(m => m.Entries[entryIndex].Selected)
|
||||
@Html.HiddenFor(m => m.Entries[entryIndex].Field)
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.Entries[entryIndex].Selected)">@Model.Entries[entryIndex].Field</label>
|
||||
</li>
|
||||
entryIndex = entryIndex + 1;
|
||||
if (String.IsNullOrWhiteSpace(Model.SelectedIndex)) {
|
||||
Model.Entries.Insert(0, new IndexSettingsEntry { Index = "" });
|
||||
}
|
||||
</ul>
|
||||
|
||||
<select id="selectIndex" name="@Html.NameFor(m => m.SelectedIndex)">
|
||||
@foreach (var modelEntry in Model.Entries) {
|
||||
@Html.SelectOption(Model.SelectedIndex, modelEntry.Index, modelEntry.Index)
|
||||
}
|
||||
</select>
|
||||
<span class="hint">@T("Select which index to use in search queries.")</span>
|
||||
|
||||
<label>@T("Fields")</label>
|
||||
<ul>
|
||||
@{var entryIndex = 0;}
|
||||
@foreach(var modelEntry in Model.Entries) {
|
||||
@Html.HiddenFor(m => m.Entries[entryIndex].Index)
|
||||
<li data-index="@modelEntry.Index">
|
||||
@if (modelEntry.Fields != null && modelEntry.Fields.Any()) {
|
||||
<ul>
|
||||
@{ var fieldIndex = 0;}
|
||||
@foreach (var fieldEntry in Model.Entries[entryIndex].Fields) {
|
||||
<li>
|
||||
@Html.EditorFor(m => m.Entries[entryIndex].Fields[fieldIndex].Selected)
|
||||
@Html.HiddenFor(m => m.Entries[entryIndex].Fields[fieldIndex].Field)
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.Entries[entryIndex].Fields[fieldIndex].Selected)">@Model.Entries[entryIndex].Fields[fieldIndex].Field</label>
|
||||
</li>
|
||||
fieldIndex++;
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</li>
|
||||
entryIndex++;
|
||||
}
|
||||
</ul>
|
||||
<span class="hint">@T("Check any property which should be used for search queries.")</span>
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
<span class="hint">@T("There are currently no fields to search from. Please update you index, and check some indexable content exists.")</span>
|
||||
<span class="hint">@T("There is currently no index to search from. Please update you index, and check some indexable content exists.")</span>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
@@ -33,3 +60,20 @@
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
|
||||
@using (Script.Foot()) {
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
(function ($) {
|
||||
|
||||
$('li[data-index]').hide();
|
||||
$("li[data-index='@Model.SelectedIndex']").show();
|
||||
$('#selectIndex').change(function () {
|
||||
$('li[data-index]').hide();
|
||||
$("li[data-index='" + $(this).val() + "']").show();
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
//]]>
|
||||
</script>
|
||||
}
|
@@ -19,12 +19,12 @@
|
||||
|
||||
<Metadata>
|
||||
<Types>
|
||||
<Page ContentTypeSettings.Draftable="True" TypeIndexing.Included="true">
|
||||
<Page ContentTypeSettings.Draftable="True" TypeIndexing.Indexes="Search">
|
||||
<TagsPart />
|
||||
<LocalizationPart />
|
||||
<AutoroutePart />
|
||||
</Page>
|
||||
<BlogPost ContentTypeSettings.Draftable="True" TypeIndexing.Included="true">
|
||||
<BlogPost ContentTypeSettings.Draftable="True" TypeIndexing.Indexes="Search">
|
||||
<CommentsPart />
|
||||
<TagsPart />
|
||||
<LocalizationPart />
|
||||
|
@@ -18,13 +18,13 @@
|
||||
|
||||
<Metadata>
|
||||
<Types>
|
||||
<Page ContentTypeSettings.Draftable="True" TypeIndexing.Included="true">
|
||||
<Page ContentTypeSettings.Draftable="True" TypeIndexing.Indexes="Search">
|
||||
<TagsPart />
|
||||
<LocalizationPart />
|
||||
<TitlePart/>
|
||||
<AutoroutePart />
|
||||
</Page>
|
||||
<BlogPost ContentTypeSettings.Draftable="True" TypeIndexing.Included="true">
|
||||
<BlogPost ContentTypeSettings.Draftable="True" TypeIndexing.Indexes="Search">
|
||||
<CommentsPart />
|
||||
<TagsPart />
|
||||
<LocalizationPart />
|
||||
|
@@ -13,11 +13,11 @@ namespace Orchard.Indexing {
|
||||
#region IIndexManager Members
|
||||
|
||||
public bool HasIndexProvider() {
|
||||
return _indexProviders.AsQueryable().Count() > 0;
|
||||
return _indexProviders.Any();
|
||||
}
|
||||
|
||||
public IIndexProvider GetSearchIndexProvider() {
|
||||
return _indexProviders.AsQueryable().FirstOrDefault();
|
||||
return _indexProviders.FirstOrDefault();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace Orchard.Indexing {
|
||||
public static class MetaDataExtensions {
|
||||
public static ContentTypeDefinitionBuilder Indexed(this ContentTypeDefinitionBuilder builder) {
|
||||
return builder.WithSetting("TypeIndexing.Included", "true");
|
||||
public static ContentTypeDefinitionBuilder Indexed(this ContentTypeDefinitionBuilder builder, params string[] indexes) {
|
||||
return builder.WithSetting("TypeIndexing.Indexes", string.Join(",", indexes ?? new string[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user