5
votes

I am trying to perform an exact match on the stored value (rather than the query value).

My indexed data is something like: "banana republic"

I need the following queries to match:

  • shirts from banana republic
  • banana republic items
  • stuff banana republic and other stuff

These should not match:

  • banana not republic
  • banana is a good fruit
  • Chile republic

My scenario is actually simple and easily doable with SQL using a query similar to: query_string LIKE '%stored_val%' but I couldn't configure the anlayzers to perform it. I am sure EdgeNGramFilter can achieve this, but it would be very expensive to create ngrams that are more than 30 characters long.

My current implementation is: In the index analyzer, use solr.KeywordTokenizerFactory. In the query analyzer, use solr.ShingleFilterFactory (2-4 tokens). Everything looks fine when using the analysis tool. But using the query API, the query gets translated to:

rawquerystring: "match_name:"banana republic"",
parsedquery_toString: "match_name:"(banana bananarepublic) republic""

which does not match my stored token "bananarepublic"

My analysis chain looks like:

  <fieldType name="singletoken" class="solr.TextField">
    <analyzer type="index">
      <charFilter class="solr.MappingCharFilterFactory" mapping="../../common-config/mapping-ISOLatin1Accent.txt"/> <!-- map accented letters to their ascii equivilants -->
      <tokenizer class="solr.KeywordTokenizerFactory"/>
      <filter class="solr.LowerCaseFilterFactory" />
      <filter class="solr.PatternReplaceFilterFactory" pattern="[^A-Za-z0-9&amp; ]" replacement=" "/>
      <filter class="solr.PatternReplaceFilterFactory" pattern="(^\s+|\s+$)" replacement=""/> <!-- join everything in a single token with no spaces -->
    </analyzer>
    <analyzer type="query">
      <charFilter class="solr.MappingCharFilterFactory" mapping="../../common-config/mapping-ISOLatin1Accent.txt"/> <!-- map accented letters to their ascii equivilants -->
      <tokenizer class="solr.WhitespaceTokenizerFactory" />
      <filter class="solr.WordDelimiterFilterFactory" /> <!-- using all default options from: https://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters#solr.WordDelimiterFilterFactory -->
      <filter class="solr.LowerCaseFilterFactory" />
      <filter class="solr.PatternReplaceFilterFactory" pattern="[^A-Za-z0-9&amp; ]" replacement=" "/>
      <filter class="solr.TrimFilterFactory" />
      <filter class="solr.ShingleFilterFactory" minShingleSize="2" maxShingleSize="4" outputUnigrams="true" tokenSeparator="" />
    </analyzer>
  </fieldType>
2
Hi @mjalajel did you end up solving your problem? I would love to know the result. Thanks - mils

2 Answers

0
votes

In 4.x and trunk the default value of mm is dictated by the q.op param (q.op=AND => mm=100%; q.op=OR => mm=0%). Keep in mind the default operator is effected by your schema.xml entry. In older versions of Solr the default value is 100% (all clauses must match)

Did you use this syntax in your query ?

your_domain:8983/solr/select/?q=banana+republic&defType=dismax&q.op=AND&...

OR

your_domain:8983/solr/select/?q=banana+republic&defType=dismax&mm=100%&...

0
votes

There is no way to solve this problem without resorting to custom query Tokenizers, because the problem resides in the tokenization phase.

lets say that we have this phrase: banana republic and we want to index and query this exact phrase. what is actually happening is:

Index Time: when solr is indexing banana republic the tokenizer will split this phrase into two separate phrases so the index will look like this

Phrase: banana

Phrase: republic

Query Time: at query time you'll also be searching for two separate phrases joined by OR operation.

What you actually need is something like this:

Index: Phrase: banana republic as one token.

Query: search for Phrase: banana republic as one token.

The best solution is to use is to use a custom phrase tokenizers like this one: https://github.com/lucidworks/auto-phrase-tokenfilter