├── .gitattributes
├── .github
└── workflows
│ └── CI.yml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── NuGet.config
├── README.md
├── SmartSolrSchema.sln
├── build
├── PackageNuGet.cmd
├── PackageNuGet.ps1
└── Push NuGet.ps1
├── src
└── SmartSolrSchema
│ ├── App_Config
│ └── Modules
│ │ └── SmartSolrSchema
│ │ ├── DateTimeSortable.config
│ │ └── PopulateSolrSchema.config
│ ├── ComputedFields
│ └── GenericDateField.cs
│ ├── Pipelines
│ └── SolrProvider
│ │ └── PopulateSolrSchemaHelper.cs
│ └── SmartSolrSchema.csproj
└── tools
└── NuGet.exe
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.cs text=auto diff=csharp
2 | *.html text=auto
3 | *.htm text=auto
4 | *.css text=auto
5 | *.scss text=auto
6 | *.sass text=auto
7 | *.less text=auto
8 | *.js text=auto
9 | *.sql text=auto
10 |
11 | *.csproj text=auto merge=union
12 | *.sln text=auto eol=crlf merge=union
13 |
14 | *.item -text
15 |
16 | *.config linguist-language=XML
--------------------------------------------------------------------------------
/.github/workflows/CI.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: windows-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Setup .NET Core
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: 3.1.101
20 | - name: Build
21 | run: dotnet build --configuration Release
22 | - name: Test
23 | run: dotnet test
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ######## GENERAL IGNORES #######
2 |
3 | # ignore logs, OS cache files
4 | *.log
5 | .DS_Store*
6 | ehthumbs.db
7 | Icon?
8 | Thumbs.db
9 | .vs
10 |
11 | ######## GENERAL .NET IGNORES ########
12 |
13 | # Ignore binaries in the source
14 | src/*/bin
15 | src/*/obj
16 | lib/sitecore/*
17 | !lib/sitecore/readme.txt
18 | src/Dianoga.Tests/Optimizers/Pipelines/DianogaSvg/SVGO
19 |
20 | # Ignore user-scoped solution and project settings
21 | *.user
22 | *.suo
23 |
24 | # NuGet packages
25 | packages/*
26 | !packages/repositories.config
27 | Build/*.nupkg
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Welcome, and thank you for your interest in contributing !
4 |
5 | There are many ways in which you can contribute, beyond writing code. The goal of this document is to provide a high-level overview of how you can get involved.
6 |
7 | ## Providing Feedback
8 |
9 | Your comments and feedback are welcome - you can open an issue, or reach out on [Sitecore Slack](https://sitecore.chat/)
10 |
11 | ## Reporting Issues
12 |
13 | Have you identified a reproducible problem? Have a feature request? We want to hear about it! Here's how you can make reporting your issue as effective as possible.
14 |
15 | ### Look For an Existing Issue
16 |
17 | Before you create a new issue, please do a search in issues to see if the issue or feature request has already been filed.
18 |
19 | If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment:
20 |
21 | * 👍 - upvote
22 | * 👎 - downvote
23 |
24 | If you cannot find an existing issue that describes your bug or feature, create a new issue using the guidelines below.
25 |
26 | ### Writing Good Bug Reports and Feature Requests
27 |
28 | File a single issue per problem and feature request. Do not enumerate multiple bugs or feature requests in the same issue.
29 |
30 | Do not add your issue as a comment to an existing issue unless it's for the identical input. Many issues look similar, but have different causes.
31 |
32 | The more information you can provide, the more likely someone will be successful at reproducing the issue and finding a fix.
33 |
34 | Please include the following with each issue:
35 |
36 | * Version
37 |
38 | * Environment description
39 |
40 | * What configs you have enabled
41 |
42 | * Reproducible steps (1... 2... 3...) that cause the issue
43 |
44 | * What you expected to see, versus what you actually saw
45 |
46 | * Relevant logs
47 |
48 | # Thank You!
49 |
50 | Your contributions to open source, large or small, make great projects like this possible. Thank you for taking the time to contribute.
51 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Mark Gibbons (Triggerfish)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/NuGet.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SmartSolrSchema
2 |
3 | This module enhances the built-in Sitecore Solr Populate Managed Schema functionality by populating not only standard Sitecore dynamic fields but also reads any custom languages that are set up in the Sitecore Master database under `/sitecore/system/languages`.
4 |
5 | ## Why?
6 |
7 | If you add a new language to Sitecore, for example Korean, and start adding items based on that language, when it comes to indexing on Solr you will see issues about missing dynamic fields about the `ko` language.
8 |
9 | Example:
10 |
11 | > org.apache.solr.common.SolrException: ERROR: [doc=sitecore://master/{bfde3d21-3a67-4938-aa90-33da9caf7bf5}?lang=cs-cz&ver=1] unknown field '__display_name_t_cs'
12 |
13 | Usually you would then need to manually patch the Solr schema yourself [like this](https://sitecore.stackexchange.com/a/2042/1278).
14 |
15 | That is a very manual process and makes the DevOps side of things much more tricky. So let's automate it!
16 |
17 | ### Date *_dtm Bugfix
18 |
19 | This module also fixes a Sitecore bug where Solr datetimeCollection has fieldNameFormat="{0}_dtm" but the schema builder format is "*_tdtm"
20 |
21 | ### Date Sorting
22 |
23 | There is a somewhat common issue with sorting by date fields in Solr where it does not always sort correctly. It happens more often if you have a lot of items that you are searching and ordering.
24 | This has been blogged about [here](https://www.sitecorenutsbolts.net/2014/11/06/Sitecore-Sorting-by-Date-with-SOLR/) by Rich Seal, where the fix is to add a date field to the solr managed schema that is `indexed="true" stored="false"`.
25 | I have made this easier for you by automatically adding a `*_tdts` dynamic field with this setting. You can then use it in your project as follows:
26 |
27 | 1. Open the `App_Config/Modules/SmartSolrSchema/DateTimeSortable.config` and uncomment the example computed field. Update the `fieldName` parameter with your field name.
28 | 2. Add as many fields as you need to sort by here.
29 | 3. Build and deploy your solution and do a full index rebuild
30 | 4. You can now sort by `fieldname_tdts` which will return accurate results.
31 |
32 | ## Sitecore Version Support
33 |
34 | * Sitecore 10.1.x and later - Supported - Use the [SmartSolrSchema.SC101](https://www.nuget.org/packages/SmartSolrSchema.SC101) nuget package
35 | * Sitecore 9.1.x to 10.0.x - Supported - Use the [SmartSolrSchema.SC91-100](https://www.nuget.org/packages/SmartSolrSchema.SC91-100) nuget package
36 | * Sitecore 9.x and earlier - Not Supported
37 |
38 | ## How to install
39 |
40 | 1. Install the nuget package.
41 | 2. If you're using PackageReferences then you'll also need to copy in the configs in the package `App_Config/Modules/SmartSolrSchema`
42 | 3. Build your solution and ensure the `SmartSolrSchema.dll` and configs are deployed
43 |
44 | ## How to use
45 |
46 | 1. Log into Sitecore and go to the Control Panel
47 | 2. Open the Populate Solr Managed Schema window
48 | 3. Check the indexes you want to populate and click Populate
49 | 4. Ensure that the process Succeeded. You can also check the logs to see what custom languages have been populated. For example: `SmartSolrSchema: adding custom defined language to schema ko`
50 | 5. Close the dialog and open the Indexing Manager
51 | 6. Rebuild the indexes
52 | 7. You can check the Crawling log file to make sure you're not getting errors about unknown fields.
53 |
--------------------------------------------------------------------------------
/SmartSolrSchema.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29613.14
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3FE4508D-F5FC-48B8-AC07-4DC12C01215D}"
7 | ProjectSection(SolutionItems) = preProject
8 | README.md = README.md
9 | EndProjectSection
10 | EndProject
11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SmartSolrSchema", "src\SmartSolrSchema\SmartSolrSchema.csproj", "{34C224F9-63EB-4D3B-A50D-4D4C185EDD1B}"
12 | EndProject
13 | Global
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Debug|Any CPU = Debug|Any CPU
16 | Release|Any CPU = Release|Any CPU
17 | EndGlobalSection
18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 | {34C224F9-63EB-4D3B-A50D-4D4C185EDD1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {34C224F9-63EB-4D3B-A50D-4D4C185EDD1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {34C224F9-63EB-4D3B-A50D-4D4C185EDD1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {34C224F9-63EB-4D3B-A50D-4D4C185EDD1B}.Release|Any CPU.Build.0 = Release|Any CPU
23 | EndGlobalSection
24 | GlobalSection(SolutionProperties) = preSolution
25 | HideSolutionNode = FALSE
26 | EndGlobalSection
27 | GlobalSection(ExtensibilityGlobals) = postSolution
28 | SolutionGuid = {683C6893-BEAE-40BE-A611-050AEC6B9DC7}
29 | EndGlobalSection
30 | EndGlobal
31 |
--------------------------------------------------------------------------------
/build/PackageNuGet.cmd:
--------------------------------------------------------------------------------
1 | @ECHO off
2 |
3 | SET scriptRoot=%~dp0
4 |
5 | powershell.exe -ExecutionPolicy Unrestricted -NoExit .\PackageNuGet.ps1 %scriptRoot%
--------------------------------------------------------------------------------
/build/PackageNuGet.ps1:
--------------------------------------------------------------------------------
1 | param($scriptRoot)
2 |
3 | $ErrorActionPreference = "Stop"
4 |
5 | function Resolve-MsBuild {
6 | $msb2017 = Resolve-Path "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\*\MSBuild\*\bin\msbuild.exe" -ErrorAction SilentlyContinue
7 | if($msb2017) {
8 | Write-Host "Found MSBuild 2019 (or later)."
9 | Write-Host $msb2017
10 | return $msb2017
11 | }
12 |
13 | $msBuild2015 = "${env:ProgramFiles(x86)}\MSBuild\14.0\bin\msbuild.exe"
14 |
15 | if(-not (Test-Path $msBuild2015)) {
16 | throw 'Could not find MSBuild 2015 or later.'
17 | }
18 |
19 | Write-Host "Found MSBuild 2015."
20 | Write-Host $msBuild2015
21 |
22 | return $msBuild2015
23 | }
24 |
25 | $msBuild = Resolve-MsBuild
26 | $nuGet = "$scriptRoot..\tools\NuGet.exe"
27 | $solution = "$scriptRoot\..\SmartSolrSchema.sln"
28 |
29 | & $nuGet restore $solution
30 | & $msBuild $solution /p:Configuration=Release /t:Rebuild /m
31 |
32 | & dotnet pack "$scriptRoot\..\src\SmartSolrSchema\SmartSolrSchema.csproj" -Property:Configuration=Release -o $scriptRoot --include-symbols
--------------------------------------------------------------------------------
/build/Push NuGet.ps1:
--------------------------------------------------------------------------------
1 | gci *.nupkg -exclude *.symbols.nupkg | % { ..\tools\NuGet.exe push $_ -Source https://api.nuget.org/v3/index.json }
--------------------------------------------------------------------------------
/src/SmartSolrSchema/App_Config/Modules/SmartSolrSchema/DateTimeSortable.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/SmartSolrSchema/App_Config/Modules/SmartSolrSchema/PopulateSolrSchema.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SmartSolrSchema.Pipelines.SolrProvider.DefaultPopulateHelperFactory, SmartSolrSchema
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/SmartSolrSchema/ComputedFields/GenericDateField.cs:
--------------------------------------------------------------------------------
1 | using Sitecore.ContentSearch;
2 | using Sitecore.ContentSearch.ComputedFields;
3 | using Sitecore.Data.Fields;
4 |
5 | namespace SmartSolrSchema.ComputedFields
6 | {
7 | public class GenericDateField : IComputedIndexField
8 | {
9 | public string FieldName { get; set; }
10 | public string ReturnType { get; set; }
11 |
12 | public object ComputeFieldValue(IIndexable indexable)
13 | {
14 | var indexItem = indexable as SitecoreIndexableItem;
15 | if (indexItem?.Item == null) return null;
16 | var item = indexItem.Item;
17 |
18 | if (string.IsNullOrEmpty(item.Fields[FieldName]?.Value)) return null;
19 |
20 | DateField fieldValue = item.Fields[FieldName];
21 | if ((fieldValue == null) || (fieldValue.InnerField.Value == null)) return null;
22 |
23 | return fieldValue.DateTime;
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/src/SmartSolrSchema/Pipelines/SolrProvider/PopulateSolrSchemaHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Xml.Linq;
4 | using Sitecore.Configuration;
5 | using Sitecore.ContentSearch.SolrProvider.Pipelines.PopulateSolrSchema;
6 | using Sitecore.Diagnostics;
7 | using SolrNet.Schema;
8 |
9 | namespace SmartSolrSchema.Pipelines.SolrProvider
10 | {
11 | public class PopulateSolrSchemaHelper : ISchemaPopulateHelper
12 | {
13 | private readonly SolrSchema solrSchema;
14 |
15 | public PopulateSolrSchemaHelper(SolrSchema solrSchema)
16 | {
17 | Assert.ArgumentNotNull(solrSchema, "solrSchema");
18 | this.solrSchema = solrSchema;
19 | }
20 |
21 | public virtual IEnumerable GetAllFields()
22 | {
23 | var addFields = GetAddFields().Where(x => x != null).ToList();
24 | var langSpecific = GetAddFieldsLangSpecific(addFields);
25 | return from o in GetRemoveFields().Union(addFields).Union(langSpecific)
26 | where o != null
27 | select o;
28 | }
29 |
30 | public virtual IEnumerable GetAllFieldTypes()
31 | {
32 | return from o in GetReplaceFields().Union(GetAddFieldTypes())
33 | where o != null
34 | select o;
35 | }
36 |
37 | protected virtual bool TypeExists(string type)
38 | {
39 | return solrSchema.FindSolrFieldTypeByName(type) != null;
40 | }
41 |
42 | protected XElement CreateField(string name, string type, bool required, bool indexed, bool stored, bool multiValued, bool omitNorms, bool termVectors, bool termPositions, bool termOffsets, string defaultValue = null, bool isDynamic = false)
43 | {
44 | if (!TypeExists(type))
45 | {
46 | return null;
47 | }
48 |
49 | string expandedName = isDynamic ? "add-dynamic-field" : "add-field";
50 | XElement xElement = new XElement(expandedName);
51 | xElement.Add(new XElement("name", name));
52 | xElement.Add(new XElement("type", type));
53 | xElement.Add(new XElement("indexed", indexed.ToString().ToLowerInvariant()));
54 | xElement.Add(new XElement("stored", stored.ToString().ToLowerInvariant()));
55 | if (required)
56 | {
57 | xElement.Add(new XElement("required", true));
58 | }
59 |
60 | if (multiValued)
61 | {
62 | xElement.Add(new XElement("multiValued", true));
63 | }
64 |
65 | if (omitNorms)
66 | {
67 | xElement.Add(new XElement("omitNorms", true));
68 | }
69 |
70 | if (termVectors)
71 | {
72 | xElement.Add(new XElement("termVectors", true));
73 | }
74 |
75 | if (termPositions)
76 | {
77 | xElement.Add(new XElement("termPositions", true));
78 | }
79 |
80 | if (termOffsets)
81 | {
82 | xElement.Add(new XElement("termOffsets", true));
83 | }
84 |
85 | if (!string.IsNullOrEmpty(defaultValue))
86 | {
87 | xElement.Add(new XElement("default", defaultValue));
88 | }
89 |
90 | return xElement;
91 | }
92 |
93 | protected XElement CreateFieldType(string name, string @class, IDictionary properties)
94 | {
95 | XElement xElement = new XElement(TypeExists(name) ? "replace-field-type" : "add-field-type");
96 | xElement.Add(new XElement("name", name));
97 | xElement.Add(new XElement("class", @class));
98 | foreach (KeyValuePair property in properties)
99 | {
100 | xElement.Add(new XElement(property.Key, property.Value));
101 | }
102 |
103 | return xElement;
104 | }
105 |
106 | private static XElement GetRemoveField(string name, bool isDynamicField = false)
107 | {
108 | Assert.ArgumentNotNull(name, "name");
109 | XElement xElement = new XElement(isDynamicField ? "delete-dynamic-field" : "delete-field");
110 | xElement.Add(new XElement("name", name));
111 | return xElement;
112 | }
113 |
114 | private static XElement GetRemoveCopyField(string source, string destination)
115 | {
116 | Assert.ArgumentNotNull(source, "source");
117 | Assert.ArgumentNotNull(destination, "destination");
118 | XElement xElement = new XElement("delete-copy-field");
119 | xElement.Add(new XElement("source", source));
120 | xElement.Add(new XElement("dest", destination));
121 | return xElement;
122 | }
123 |
124 | private IEnumerable GetRemoveFields()
125 | {
126 | foreach (SolrNet.Schema.SolrCopyField solrCopyField in solrSchema.SolrCopyFields)
127 | {
128 | yield return GetRemoveCopyField(solrCopyField.Source, solrCopyField.Destination);
129 | }
130 |
131 | foreach (SolrDynamicField solrDynamicField in solrSchema.SolrDynamicFields)
132 | {
133 | yield return GetRemoveField(solrDynamicField.Name, isDynamicField: true);
134 | }
135 |
136 | foreach (SolrField solrField in solrSchema.SolrFields)
137 | {
138 | yield return GetRemoveField(solrField.Name);
139 | }
140 | }
141 |
142 | private IEnumerable GetAddFieldTypes()
143 | {
144 | yield return CreateFieldType("random", "solr.RandomSortField", new Dictionary
145 | {
146 | {
147 | "indexed",
148 | "true"
149 | }
150 | });
151 | yield return CreateFieldType("ignored", "solr.StrField", new Dictionary
152 | {
153 | {
154 | "indexed",
155 | "false"
156 | },
157 | {
158 | "stored",
159 | "false"
160 | },
161 | {
162 | "docValues",
163 | "false"
164 | },
165 | {
166 | "multiValued",
167 | "true"
168 | }
169 | });
170 | }
171 |
172 | private IEnumerable GetAddFields()
173 | {
174 | yield return CreateField("_content", "text_general", required: false, indexed: true, stored: false, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
175 | yield return CreateField("_database", "lowercase", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
176 | yield return CreateField("_path", "string", required: false, indexed: true, stored: true, multiValued: true, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
177 | yield return CreateField("_uniqueid", "string", required: true, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
178 | yield return CreateField("_datasource", "lowercase", required: true, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
179 | yield return CreateField("_parent", "string", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
180 | yield return CreateField("_name", "text_general", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
181 | yield return CreateField("_displayname", "text_general", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
182 | yield return CreateField("_language", "string", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
183 | yield return CreateField("_creator", "lowercase", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
184 | yield return CreateField("_editor", "lowercase", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
185 | yield return CreateField("_created", "pdate", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
186 | yield return CreateField("_updated", "pdate", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
187 | yield return CreateField("_hidden", "boolean", required: false, indexed: true, stored: false, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
188 | yield return CreateField("_template", "lowercase", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
189 | yield return CreateField("_templatename", "lowercase", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
190 | yield return CreateField("_templates", "string", required: false, indexed: true, stored: true, multiValued: true, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
191 | yield return CreateField("_icon", "lowercase", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
192 | yield return CreateField("_links", "lowercase", required: false, indexed: true, stored: true, multiValued: true, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
193 | yield return CreateField("_tags", "lowercase", required: false, indexed: true, stored: true, multiValued: true, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
194 | yield return CreateField("_group", "string", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
195 | yield return CreateField("_indexname", "lowercase", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
196 | yield return CreateField("_latestversion", "boolean", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
197 | yield return CreateField("_indextimestamp", "pdate", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, "NOW");
198 | yield return CreateField("_fullpath", "lowercase", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
199 | yield return CreateField("_isclone", "boolean", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
200 | yield return CreateField("_version", "string", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
201 | yield return CreateField("_hash", "string", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
202 | yield return CreateField("__semantics", "string", required: false, indexed: true, stored: true, multiValued: true, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
203 | yield return CreateField("__boost", "pfloat", required: false, indexed: true, stored: true, multiValued: false, omitNorms: true, termVectors: false, termPositions: false, termOffsets: false, "0");
204 | yield return CreateReadAccessField();
205 | yield return CreateField("lock", "boolean", required: false, indexed: true, stored: false, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
206 | yield return CreateField("__bucketable", "boolean", required: false, indexed: true, stored: false, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
207 | yield return CreateField("__workflow_state", "string", required: false, indexed: true, stored: false, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
208 | yield return CreateField("__is_bucket", "boolean", required: false, indexed: true, stored: false, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
209 | yield return CreateField("is_displayed_in_search_results", "boolean", required: false, indexed: true, stored: false, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
210 | yield return CreateField("text", "text_general", required: false, indexed: true, stored: false, multiValued: true, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
211 | yield return CreateField("text_rev", "text_general_rev", required: false, indexed: true, stored: false, multiValued: true, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
212 | yield return CreateField("alphaNameSort", "alphaOnlySort", required: false, indexed: true, stored: false, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
213 | yield return CreateField("__hidden", "boolean", required: false, indexed: true, stored: false, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
214 | yield return CreateField("_version_", "plong", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
215 | yield return CreateField("*_t", "text_general", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
216 | yield return CreateField("*_t_en", "text_en", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
217 | yield return CreateField("*_t_ar", "text_ar", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
218 | yield return CreateField("*_t_bg", "text_bg", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
219 | yield return CreateField("*_t_ca", "text_ca", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
220 | yield return CreateField("*_t_cs", "text_cz", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
221 | yield return CreateField("*_t_da", "text_da", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
222 | yield return CreateField("*_t_de", "text_de", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
223 | yield return CreateField("*_t_el", "text_el", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
224 | yield return CreateField("*_t_es", "text_es", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
225 | yield return CreateField("*_t_eu", "text_eu", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
226 | yield return CreateField("*_t_fa", "text_fa", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
227 | yield return CreateField("*_t_fi", "text_fi", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
228 | yield return CreateField("*_t_fr", "text_fr", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
229 | yield return CreateField("*_t_ga", "text_ga", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
230 | yield return CreateField("*_t_gl", "text_gl", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
231 | yield return CreateField("*_t_hi", "text_hi", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
232 | yield return CreateField("*_t_hu", "text_hu", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
233 | yield return CreateField("*_t_hy", "text_hy", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
234 | yield return CreateField("*_t_id", "text_id", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
235 | yield return CreateField("*_t_it", "text_it", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
236 | yield return CreateField("*_t_ja", "text_ja", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
237 | yield return CreateField("*_t_lv", "text_lv", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
238 | yield return CreateField("*_t_nl", "text_nl", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
239 | yield return CreateField("*_t_nb", "text_no", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
240 | yield return CreateField("*_t_pt", "text_pt", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
241 | yield return CreateField("*_t_ro", "text_ro", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
242 | yield return CreateField("*_t_ru", "text_ru", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
243 | yield return CreateField("*_t_sv", "text_sv", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
244 | yield return CreateField("*_t_th", "text_th", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
245 | yield return CreateField("*_t_tr", "text_tr", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
246 | yield return CreateField("*_i", "pint", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
247 | yield return CreateField("*_s", "string", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
248 | yield return CreateField("*_sm", "string", required: false, indexed: true, stored: true, multiValued: true, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
249 | yield return CreateField("*_ls", "lowercase", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
250 | yield return CreateField("*_lsm", "lowercase", required: false, indexed: true, stored: true, multiValued: true, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
251 | yield return CreateField("*_im", "pint", required: false, indexed: true, stored: true, multiValued: true, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
252 | yield return CreateField("*_txm", "text_general", required: false, indexed: true, stored: true, multiValued: true, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
253 | yield return CreateField("*_b", "boolean", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
254 | yield return CreateField("*_dt", "pdate", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
255 | yield return CreateField("*_p", "location", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
256 | yield return CreateField("*_ti", "pint", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
257 | yield return CreateField("*_tl", "plong", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
258 | yield return CreateField("*_tf", "pfloat", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
259 | yield return CreateField("*_td", "pdouble", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
260 | yield return CreateField("*_tdt", "pdate", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
261 | yield return CreateField("*_tdts", "pdate", required: false, indexed: true, stored: false, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
262 | yield return CreateField("*_tdtm", "pdate", required: false, indexed: true, stored: true, multiValued: true, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
263 | yield return CreateField("*_pi", "pint", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
264 | yield return CreateField("*_c", "currency", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
265 | yield return CreateField("*_ignored", "ignored", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
266 | yield return CreateField("*_random", "random", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
267 | yield return CreateField("*_rpt", "location_rpt", required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
268 | }
269 |
270 | private List GetAddFieldsLangSpecific(List addFields)
271 | {
272 | var langs = Factory.GetDatabase("master")
273 | .GetItem("/sitecore/system/Languages")
274 | .Children
275 | .Select(x => x["Iso"])
276 | .Where(x => !string.IsNullOrEmpty(x))
277 | .Select(x => $"*_t_{x}")
278 | .ToList()
279 | .Distinct();
280 | var addFieldsLangs = addFields
281 | .Select(x => x.Element("name")?.Value)
282 | .Where(x => !string.IsNullOrEmpty(x))
283 | .ToList();
284 | var toAdd = langs
285 | .Where(x => !addFieldsLangs.Contains(x))
286 | .ToList();
287 | var result = new List();
288 | foreach (var fieldname in toAdd)
289 | {
290 | var lang = fieldname.Replace("*_t_", "");
291 | result.Add(CreateDynamicFieldWithFallbackFieldType(fieldname, $"text_{lang}"));
292 | Sitecore.Diagnostics.Log.Info($"SmartSolrSchema: adding custom defined language to schema {lang}", this);
293 | }
294 | return result;
295 | }
296 |
297 | private XElement CreateReadAccessField()
298 | {
299 | XElement xElement = CreateField("_readaccess", "lowercase", required: false, indexed: true, stored: false, multiValued: true, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false);
300 | xElement.Add(new XElement("docValues", false));
301 | return xElement;
302 | }
303 |
304 | private XElement CreateDynamicFieldWithFallbackFieldType(string fieldName, string fieldType, string fallbackFieldType = "text_general")
305 | {
306 | Assert.ArgumentNotNull(fieldName, "fieldName");
307 | Assert.ArgumentNotNull(fieldType, "fieldType");
308 | string type = TypeExists(fieldType) ? fieldType : fallbackFieldType;
309 | return CreateField(fieldName, type, required: false, indexed: true, stored: true, multiValued: false, omitNorms: false, termVectors: false, termPositions: false, termOffsets: false, null, isDynamic: true);
310 | }
311 |
312 | private IEnumerable GetReplaceFields()
313 | {
314 | yield return GetReplaceTextGeneralFieldType();
315 | }
316 |
317 | private XElement GetReplaceTextGeneralFieldType()
318 | {
319 | SolrFieldType solrFieldType = solrSchema.SolrFieldTypes.Find((SolrFieldType f) => f.Name == "text_general");
320 | if (solrFieldType == null)
321 | {
322 | return null;
323 | }
324 |
325 | XElement xElement = new XElement("replace-field-type");
326 | xElement.Add(new XElement("name", solrFieldType.Name));
327 | xElement.Add(new XElement("class", solrFieldType.Type));
328 | xElement.Add(new XElement("positionIncrementGap", "100"));
329 | xElement.Add(new XElement("multiValued", "false"));
330 | XElement xElement2 = new XElement("indexAnalyzer");
331 | xElement2.Add(new XElement("tokenizer", new XElement("class", "solr.StandardTokenizerFactory")));
332 | xElement2.Add(new XElement("filters", new XElement("class", "solr.StopFilterFactory"), new XElement("ignoreCase", "true"), new XElement("words", "stopwords.txt")));
333 | xElement2.Add(new XElement("filters", new XElement("class", "solr.LowerCaseFilterFactory")));
334 | xElement.Add(xElement2);
335 | XElement xElement3 = new XElement("queryAnalyzer");
336 | xElement3.Add(new XElement("tokenizer", new XElement("class", "solr.StandardTokenizerFactory")));
337 | xElement3.Add(new XElement("filters", new XElement("class", "solr.StopFilterFactory"), new XElement("ignoreCase", "true"), new XElement("words", "stopwords.txt")));
338 | xElement3.Add(new XElement("filters", new XElement("class", "solr.SynonymFilterFactory"), new XElement("synonyms", "synonyms.txt"), new XElement("ignoreCase", "true"), new XElement("expand", "true")));
339 | xElement3.Add(new XElement("filters", new XElement("class", "solr.LowerCaseFilterFactory")));
340 | xElement.Add(xElement3);
341 | return xElement;
342 | }
343 | }
344 |
345 | #if NET48
346 |
347 | public class DefaultPopulateHelperFactory : Sitecore.ContentSearch.SolrProvider.Abstractions.IPopulateHelperFactory
348 | {
349 | public ISchemaPopulateHelper GetPopulateHelper(SolrSchema solrSchema) => new PopulateSolrSchemaHelper(solrSchema);
350 | }
351 |
352 | #else
353 |
354 | public class PopulateFields : Sitecore.ContentSearch.SolrProvider.Pipelines.PopulateSolrSchema.PopulateFields
355 | {
356 | protected override ISchemaPopulateHelper GetHelper(SolrSchema solrSchema) => new PopulateSolrSchemaHelper(solrSchema);
357 | }
358 |
359 | #endif
360 | }
--------------------------------------------------------------------------------
/src/SmartSolrSchema/SmartSolrSchema.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | false
6 | 1.1.2
7 | Mark Gibbons
8 |
9 | MIT
10 | https://github.com/dataweaversio/SmartSolrSchema
11 |
12 | sitecore solr
13 | Copyright Dataweavers
14 |
15 |
16 | SmartSolrSchema
17 | SmartSolrSchema
18 | Dataweavers
19 |
20 | SmartSolrSchema.SC101
21 | Smart Solr Schema Populate Module for Sitecore 10.1, 10.2, 10.3
22 | git
23 |
24 |
25 |
26 | 10.1.2
27 | all
28 |
29 |
30 | 10.1.2
31 | all
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/tools/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dataweaversio/SmartSolrSchema/114483554b5767e81b9195228bbc674f5c85ba32/tools/NuGet.exe
--------------------------------------------------------------------------------