Skip to content

Instantly share code, notes, and snippets.

@lowedown
Last active April 3, 2020 13:55
Show Gist options
  • Save lowedown/ee49f96dcfd568a3dee8c7047d91c5e9 to your computer and use it in GitHub Desktop.
Save lowedown/ee49f96dcfd568a3dee8c7047d91c5e9 to your computer and use it in GitHub Desktop.
Sitecore Customize SOLR Managed Schema
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<contentSearch>
<customSolrManagedSchema>
<commands applyToIndex="my_custom_index">
<add-or-replace-field-type>
<name>ngram</name>
<class>solr.TextField</class>
<positionIncrementGap>100</positionIncrementGap>
<indexAnalyzer>
<tokenizer>
<class>solr.StandardTokenizerFactory</class>
</tokenizer>
<filters>
<class>solr.LowerCaseFilterFactory</class>
</filters>
<filters>
<class>solr.ASCIIFoldingFilterFactory</class>
<preserveOriginal>false</preserveOriginal>
</filters>
<filters>
<class>solr.NGramFilterFactory</class>
<minGramSize>2</minGramSize>
<minGramSize>35</minGramSize>
</filters>
</indexAnalyzer>
<queryAnalyzer>
<type>query</type>
<tokenizer>
<class>solr.StandardTokenizerFactory</class>
</tokenizer>
<filters>
<class>solr.LowerCaseFilterFactory</class>
</filters>
<filters>
<class>solr.ASCIIFoldingFilterFactory</class>
<preserveOriginal>false</preserveOriginal>
</filters>
</queryAnalyzer>
</add-field-type>
<add-dynamic-field>
<name>*_ngram</name>
<type>ngram</type>
<indexed>true</indexed>
<stored>true</stored>
<termVectors>true</termVectors>
<termPositions>true</termPositions>
<termOffsets>true</termOffsets>
</add-dynamic-field>
<add-dynamic-field>
<name>*_ngramm</name>
<type>ngram</type>
<indexed>true</indexed>
<stored>true</stored>
<termVectors>true</termVectors>
<termPositions>true</termPositions>
<termOffsets>true</termOffsets>
<multiValued>true</multiValued>
</add-dynamic-field>
</commands>
</customSolrManagedSchema>
</contentSearch>
<pipelines>
<contentSearch.PopulateSolrSchema>
<!-- Processor for additional SOLR schema -->
<processor type="MyProject.Pipelines.ContentSearch.PopulateSolrSchema.CustomSchemaPopulator, MyProject"
patch:after="processor[@type='Sitecore.ContentSearch.SolrProvider.Pipelines.PopulateSolrSchema.PopulateFields, Sitecore.ContentSearch.SolrProvider']"/>
</contentSearch.PopulateSolrSchema>
</pipelines>
</sitecore>
</configuration>
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Sitecore.Configuration;
using Sitecore.ContentSearch.SolrProvider.Pipelines.PopulateSolrSchema;
using Sitecore.Diagnostics;
using Sitecore.Xml;
using SolrNet.Schema;
namespace MyProject.Pipelines.ContentSearch.PopulateSolrSchema
{
public class CustomSchemaPopulator : PopulateFields
{
protected string IndexName;
public override void Process(PopulateManagedSchemaArgs args)
{
IndexName = args.Index.Name;
base.Process(args);
}
protected override ISchemaPopulateHelper GetHelper(SolrNet.Schema.SolrSchema schema)
{
Assert.ArgumentNotNull(schema, "schema");
return new CustomSchemaPopulatorHelper(schema, IndexName);
}
}
public class CustomSchemaPopulatorHelper : ISchemaPopulateHelper
{
private readonly SolrSchema _solrSchema;
private readonly string _indexName;
public CustomSchemaPopulatorHelper(SolrSchema solrSchema, string indexName)
{
_solrSchema = solrSchema;
_indexName = indexName;
}
public IEnumerable<XElement> GetAllFields()
{
var elements = new List<XElement>();
foreach (XmlNode commands in Factory.GetConfigNodes("contentSearch/customSolrManagedSchema/commands"))
{
if (IsApplicable(commands, _indexName))
{
elements.AddRange(commands.ChildNodes.Cast<XmlNode>().Select(n => ParseCommand(n.OuterXml, _solrSchema)));
}
}
return elements;
}
private XElement ParseCommand(string xml, SolrSchema schema)
{
var element = XElement.Parse(xml);
// set "add" or "replace" depending if field type already exists in schema
if (element.Name == "add-or-replace-field-type")
{
var fieldTypeName = element.Elements().FirstOrDefault(e => e.Name == "name");
if (fieldTypeName != null && !string.IsNullOrEmpty(fieldTypeName.Value))
{
if (schema.SolrFieldTypes.Any(f => f.Name == fieldTypeName.Value))
{
element = new XElement("replace-field-type", element.Attributes(), element.Elements());
}
else
{
element = new XElement("add-field-type", element.Attributes(), element.Elements());
}
}
}
return element;
}
private bool IsApplicable(XmlNode commands, string indexName)
{
var indexes = XmlUtil.GetAttribute("applyToIndex", commands);
if (string.IsNullOrEmpty(indexes))
{
return false;
}
return indexes.Split('|').Any(i => i.ToLower().Equals(indexName.ToLower()));
}
}
}
@lowedown
Copy link
Author

lowedown commented Nov 5, 2018

Update 05.11.2018: Added "add-or-replace-field-type" which automatically chooses "add" or "replace" operation depending if the field type alreaedy exists in the schema.

@tobidemski
Copy link

Hey @lowedown,

it seems sitecore included an additional function to be added "GetAllFieldTypes" to the interface of "ISchemaPopulateHelper" in sitecore 9.1 and above.

Do you think it is enough/right to return the result from the GetAllFields method?

Best regards and thanks for the share - helped me a lot to understand a bit how the new way works!
Tobi

@lowedown
Copy link
Author

lowedown commented May 8, 2019

Hi @D4rkii
Thanks for the feedback. It seems that in 9.1 the values from GetAllFieldTypes() are sent to Solr first and then the values from GetAllFields(). Maybe this is to prevent trying to add fields which do not have a type yet. In my example this doesn't happen as the add-or-replace-field-type command is processed first.
To do it right, I would run the add-field-type and replace-field-type commands in the GetAllFieldTypes() method and all the others in the GetAllFields() method. I haven't had the chance to test this on 9.1 though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment