How to overwrite the Solr document field? - solr

<arr name="itemDescSpell">
<str>Cable Tie, 4.0L (102mm), Miniature, Nyl</str>
<str>Cable Tie, 4.0L (102mm), Miniature, Nyl</str>
</arr>
itemDescSpell which is copyField which causing error when Solr Document is updated each time. I don't want to make the field as multiValued="true"
in schema , copyField is defined like below
<field name="itemDescSpell" type="textSpell"/>
<copyField source="description" dest="itemDescSpell"/>
The error is:
multiple values encountered for non multiValued field itemDescSpell.
Is anybody able to help me to solve this problem via SolrJ, while keeping this field type as textSpell?

Try using a custom UpdateRequestProcessor to overwrite the value present in the itemDescSpell field . Solr will throw the exception you get when a copyfield destination is already populated , so what you want to do is remove the copyField line from your schema and add a custom UpdateRequestProcessor to your config that could look like this :
public class CustomFactory extends UpdateRequestProcessorFactory {
#Override
public UpdateRequestProcessor getInstance(SolrQueryRequest req, SolrQueryResponse rsp, UpdateRequestProcessor next) {
return new Custom(next);
}
public class Custom extends UpdateRequestProcessor {
public Custom(UpdateRequestProcessor next){
super(next);
}
#Override
public void processAdd(AddUpdateCommand cmd) throws IOException {
cmd.solrDoc.setField("foo",cmd.solrDoc.getFieldValue("bar"));
}
}
}
This is NOT production ready code but it should give you an idea on how the final code should look. To customize the field values you can override the init method on the factory and pass those in the config.
The main difference is that solr uses addField when encountering copyField, and this class uses setField

Related

Is there a way to boost based on value of an index property in Hybris solr?

We have a requirement wherein we need to make boosting for a field dynamic. Let's say that we have an indexed property foo, we need to perform a mathematical calculation on foo to determine its boost value which would be different for every product.
Is this doable in Hybris Solr?
Note: I'm using SAP CX 2105 and Solr 8.9.0 with Dismax Query Parse.
And above requirement can be done in standalone Solr application as following.
#Resource
private SolrQuery solrQuery;
private void addBoosts()
{
solrQuery.add("boost", "sub(20, sqrt(field(foo)))");
}
In Hybris, I have tried following
#Resource
private SearchQuery searchQuery;
private void addBoosts()
{
searchQuery.addQuery("boost", "sub(20, sqrt(field(foo)))");
}
And also this
#Resource
private SearchQuery searchQuery;
private void addBoosts()
{
searchQuery.addQuery("boost", "sub(20, sqrt(field(foo)))");
}
Both don't affect search order of the result.

Hybris Solr - how to exclude certain facet values when creating SearchQueryData

I am searching in Hybris using Solr. There are certain facet values for category that I want to exclude from that specific(those categories need to be visible to other searches) search. My Solr query is as following:
q=*:*&spellcheck=true&spellcheck.dictionary=en&spellcheck.collate=true&spellcheck.q=&fq={!tag=fk6}(type\-facet_string:ANSI)&fq=(((catalogId:"ProductCatalog") AND (catalogVersion:Online)))&start=0&rows=100&facet=true&facet.field=allCategories_string_mv&facet.field={!ex=fk0}productLine_string_mv&facet.field={!ex=fk8}style-facet_string&facet.field={!ex=fk5}price_usd_string&facet.field={!ex=fk4}allPromotions_string_mv&facet.field={!ex=fk6}type-facet_string&facet.field={!ex=fk1}size-facet_string&facet.field=categoryPath_string_mv&facet.field={!ex=fk9}availableInStores_string_mv&facet.field=category_string_mv&sort=name_sortable_en_sortabletext asc,score desc&facet.mincount=1&facet.limit=-1&facet.sort=count
I don't have the ability to use raw query. All I can use is Hybris native SearchStateData and SearchQueryData. Category facet can be included in the search but I need to exclude from it i.e CategoryA and CategoryB. Right now my code just sets the value in SearchQueryData as a String in a following way:
":type-facet:" + type; or ":category:" + category
I have tried :category:(-\"CategoryA\"); but it does not end up in the final Solr query. Can anyone point me in the right direction?
In the backoffice of hybris you can install the Commerce Search Perspective with the extension backofficesolrsearch.
In this perspecetive you can enable or disable facets for searching in frontend. Is this what you are looking for?
Here is the belongig hybris wiki page to it.
Refer SearchResponseFacetsPopulator and you can override its method by creating custom populator
MySearchResponseFacetsPopulator :
public class MySearchResponseFacetsPopulator<FACET_SEARCH_CONFIG_TYPE, INDEXED_TYPE_TYPE, INDEXED_PROPERTY_TYPE, INDEXED_TYPE_SORT_TYPE, ITEM>
extends
SearchResponseFacetsPopulator<FACET_SEARCH_CONFIG_TYPE, INDEXED_TYPE_TYPE, INDEXED_PROPERTY_TYPE, INDEXED_TYPE_SORT_TYPE, ITEM>
{
#Override
public void populate(
final SolrSearchResponse<FACET_SEARCH_CONFIG_TYPE, INDEXED_TYPE_TYPE, INDEXED_PROPERTY_TYPE, SearchQuery, INDEXED_TYPE_SORT_TYPE, SearchResult> source,
final FacetSearchPageData<SolrSearchQueryData, ITEM> target)
{
super.populate(source, target);
}
#Override
protected List<FacetData<SolrSearchQueryData>> buildFacets(final SearchResult solrSearchResult,
final SolrSearchQueryData searchQueryData, final IndexedType indexedType)
{
// Do your stuff here
}
}
xml :
<alias name="defaultMyCommerceSearchResponseFacetsPopulator" alias="commerceSearchResponseFacetsPopulator" />
<bean id="defaultMyCommerceSearchResponseFacetsPopulator" class="com.my.facades.populators.MySearchResponseFacetsPopulator"
parent="defaultCommerceSearchResponseFacetsPopulator" />

Solrnet can't get mapping to work

I am using VS2012, .NET 4.5 and SolrNet. I am struggling with solrnet mappings. I've succesfully started Apache Solr with jetty on http://localhost:8983/solr. My class which I want to add to solr is:
public class Register
{
[SolrUniqueKey("id")]
public string Id { get; set; }
[SolrField("body")]
public string Body { get; set; }
}
I succesfully connect to solr, but I can't put my document into it:
Startup.Init<Register>(solrAddress);
Solr = ServiceLocator.Current.GetInstance<ISolrOperations<Register>>();
var reg = new Register
{
Id = "SP2514N",
Body = #"Dosel je prosel"
};
Solr.Add(reg);
Solr.Commit();
Here I receive error, that 'body' is unknown field. I've also used MappingManager, like this:
var mgr = new MappingManager();
var property = typeof(Register).GetProperty("Id");
mgr.Add(property, "id");
mgr.SetUniqueKey(property);
mgr.Add(typeof(Register).GetProperty("Body"), "body");
But, again, my field was not mapped. What am I doing wrong? Isn't the mapping to solr supposed to be done through code? Do I need a special xml file?
You need to confirm that you have a body field defined in your schema. If you are just using the default schema that comes with Solr, it does not include a body field. You can copy an existing similar entry in the schema.xml file, like description to get you going.
For more reference on configuring the Solr schema please refer to the following:
Solr Reference Guide - Overview of Documents, Fields and Schema Design
schema.xml - SolrTutorial.com

Custom class using TokenFilterFactory in SOLR 4.3.0

I am trying to upgrade my custom class to support SOLR 4.3.0 (from SOLR 3.5.0) hence I am trying to update my test classes to test the changes.
I got to know that we need to call the TokenFilterFactory constructor with key value pair map in SOLR 4.3.0 as below,
public class CustomFilterFactory extends TokenFilterFactory {
protected CustomFilterFactory(Map<String, String> args) {
super(args);
}
...
}
I am confused about the value that needs to be passed as key value pair. For ex:
If I have a field name as location and a custom class named com.solr.analysis.CustomFilterFactory, what should I pass as a key and value?
<fieldtype name="Location" class="solr.TextField" positionIncrementGap="100" stored="false" multiValued="true">
<filter class="com.solr.analysis.CustomFilterFactory" />
</fieldtype>
Can I do something like below in case if I am not planning to pass any other parameter to this class?
args.put(new HashMap());
CustomFilterFactory(args);
Thanks a lot for your help!!!
I just passed empty map to CustomFilterFactory method and everything worked perfectly fine.
Map<String,String> args = new HashMap<String, String>();
CustomFilterFactory factory = new CustomFilterFactory(args);
Not a solution, but a workaround. You could choose to configure a custom analyzer instead. I looked at RemoveDuplicateTokenFilter source and example configuration tried to replicate for MyCustomFilter and MyCustomFilterFactory but it didn't work. Specifically, SolrResourceLoader kept complaining about a missing init(Map<String,String>) method for my custom token filter (strangely silent for RemoveDuplicateTokenFilter). So I opted to build an Analyzer and stick that in instead. Example code would go something like this:
class MyCustomAnalyzer extends Analyzer {
#Override TokenStreamComponents createComponents(String fieldname, Reader reader) {
Tokenizer source = new WhitespaceTokenizer(Version.LUCENE_43, reader)
TokenFilter filter = new MyCustomTokenFilter(source)
new TokenStreamComponents(source, filter)
}
}
and then configure it like so:
<fieldType name="text_mc" class="solr.TextField">
<analyzer type="index" class="com...MyCustomAnalyzer"/>>
....
</fieldType>

Override "fl" parameter in Solr using SolrParams in a custom SearchComponent

I have an interesting use case for a Solr implementation we have, where there are some fields in the Solr Schema that shouldn't be returned when doing a query. The ideal solution is to change the calling program so it doesn't query for &fl=score like it does now, and only requests the necessary fields, but that won't happen in the short term so in the meantime we have to filter out some fields from the Solr response.
The approach we think has the smallest performance impact (let me know if there is a better way to do this), is to override the &fl= parameter so it lists all the fields but the ones that should be filtered out. For this, we added a new SearchComponent to the RequestHandler components list that modifies the &fl parameter. The issue we ran into with this approach is that once we get the SolrParams from the SolrQueryRequest, it cannot be modified (which is I think is the right thing to do, since it could be changing something another SearchComponent relies on). But we still need to find a way to remove these extra fields.
So, this is the code we started to write:
public void prepare(ResponseBuilder rb) throws IOException {
SolrQueryRequest req = rb.req;
SolrParams params = req.getParams();
String fl = params.get("fl");
//Remove the "fl" parameter from params and replace it with a new list:
//Cannot be done"
...
And ran into the issue of not being able to add to the SolrParams.
As a plan B, that same SearchComponent is removing the fields in the process() method, but doing it this way is slower. The code has to go through the resulting SolrDocumentList, and for each SolrDocument call removeFields(), something similar to: (simplified code)
public void process(ResponseBuilder rb) throws IOException {
...
SolrQueryResponse rsp = rb.rsp;
NamedList values = rsp.getValues();
SolrDocumentList docs = (SolrDocumentList) values.get("response");
Iterator<SolrDocument> docsIterator = sdoclist.iterator();
while (docsIterator.hasNext()) {
SolrDocument sd = sdocIterator.next();
sd.removeFields(field);
...
Any ideas on how/if this can be achieved?
Thanks for any suggestion!
With your own SearchHandler you can specify invariants (things that will always be fixed no matter the request) on any query parameter, among which there is the &fl.
It's something in the lines of:
<requestHandler name="filtered" class="solr.StandardRequestHandler">
<lst name="invariants">
<str name="fl">score,id,something_else,etc.</bool>
</lst>
</requestHandler>
More documentation:
http://wiki.apache.org/solr/SearchHandler
The only problem is that, for now, there's no negative fl parameter (i.e. return all fields except those i'm telling you). https://issues.apache.org/jira/browse/SOLR-3191
Finally, to specify which SearchHandler you want to use at query time, simply add &qt=filtered (or the name you used for it)
Try removing the fields that you don't want from the ReturnFields object.
For example, something like this:
#Override
public void process(ResponseBuilder rb) throws IOException {
String fl = rb.req.getParams().get(CommonParams.FL);
List<String> fields = Lists.newArrayList(fl.split(","));
List<String> newFields = Lists.newArrayList();
for (String field : fields) {
if (!field.equals("score")) {
newFields.add(field);
}
}
String newFl = Joiner.on(",").join(newFields);
ReturnFields returnFields = new ReturnFields(newFl, rb.req);
rb.rsp.setReturnFields(returnFields);
}
I've set the custom SearchComponent in "last-components" at solrconfig.xml.
P.S: I was using guava libraries for Lists and Joiner.

Resources