blob: a9d5c5542fdc20a035bc8a3eec6c1442b5ffd63c [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.TokenAttributes;
using Lucene.Net.Search;
using Lucene.Net.Util;
namespace Lucene.Net.QueryParsers.Classic
{
[TestFixture]
public class TestMultiAnalyzer_ : BaseTokenStreamTestCase
{
private static int multiToken = 0;
[Test]
public virtual void TestMultiAnalyzer()
{
QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "", new MultiAnalyzer());
// trivial, no multiple tokens:
assertEquals("foo", qp.Parse("foo").toString());
assertEquals("foo", qp.Parse("\"foo\"").toString());
assertEquals("foo foobar", qp.Parse("foo foobar").toString());
assertEquals("\"foo foobar\"", qp.Parse("\"foo foobar\"").toString());
assertEquals("\"foo foobar blah\"", qp.Parse("\"foo foobar blah\"").toString());
// two tokens at the same position:
assertEquals("(multi multi2) foo", qp.Parse("multi foo").toString());
assertEquals("foo (multi multi2)", qp.Parse("foo multi").toString());
assertEquals("(multi multi2) (multi multi2)", qp.Parse("multi multi").toString());
assertEquals("+(foo (multi multi2)) +(bar (multi multi2))",
qp.Parse("+(foo multi) +(bar multi)").toString());
assertEquals("+(foo (multi multi2)) field:\"bar (multi multi2)\"",
qp.Parse("+(foo multi) field:\"bar multi\"").toString());
// phrases:
assertEquals("\"(multi multi2) foo\"", qp.Parse("\"multi foo\"").toString());
assertEquals("\"foo (multi multi2)\"", qp.Parse("\"foo multi\"").toString());
assertEquals("\"foo (multi multi2) foobar (multi multi2)\"",
qp.Parse("\"foo multi foobar multi\"").toString());
// fields:
assertEquals("(field:multi field:multi2) field:foo", qp.Parse("field:multi field:foo").toString());
assertEquals("field:\"(multi multi2) foo\"", qp.Parse("field:\"multi foo\"").toString());
// three tokens at one position:
assertEquals("triplemulti multi3 multi2", qp.Parse("triplemulti").toString());
assertEquals("foo (triplemulti multi3 multi2) foobar",
qp.Parse("foo triplemulti foobar").toString());
// phrase with non-default slop:
assertEquals("\"(multi multi2) foo\"~10", qp.Parse("\"multi foo\"~10").toString());
// phrase with non-default boost:
assertEquals("\"(multi multi2) foo\"^2.0", qp.Parse("\"multi foo\"^2").toString());
// phrase after changing default slop
qp.PhraseSlop=(99);
assertEquals("\"(multi multi2) foo\"~99 bar",
qp.Parse("\"multi foo\" bar").toString());
assertEquals("\"(multi multi2) foo\"~99 \"foo bar\"~2",
qp.Parse("\"multi foo\" \"foo bar\"~2").toString());
qp.PhraseSlop=(0);
// non-default operator:
qp.DefaultOperator=(QueryParserBase.AND_OPERATOR);
assertEquals("+(multi multi2) +foo", qp.Parse("multi foo").toString());
}
[Test]
public virtual void TestMultiAnalyzerWithSubclassOfQueryParser()
{
DumbQueryParser qp = new DumbQueryParser("", new MultiAnalyzer());
qp.PhraseSlop = (99); // modified default slop
// direct call to (super's) getFieldQuery to demonstrate differnce
// between phrase and multiphrase with modified default slop
assertEquals("\"foo bar\"~99",
qp.GetSuperFieldQuery("", "foo bar", true).toString());
assertEquals("\"(multi multi2) bar\"~99",
qp.GetSuperFieldQuery("", "multi bar", true).toString());
// ask sublcass to parse phrase with modified default slop
assertEquals("\"(multi multi2) foo\"~99 bar",
qp.Parse("\"multi foo\" bar").toString());
}
[Test]
public virtual void TestPosIncrementAnalyzer()
{
#pragma warning disable 612, 618
QueryParser qp = new QueryParser(LuceneVersion.LUCENE_40, "", new PosIncrementAnalyzer());
#pragma warning restore 612, 618
assertEquals("quick brown", qp.Parse("the quick brown").toString());
assertEquals("quick brown fox", qp.Parse("the quick brown fox").toString());
}
/// <summary>
/// Expands "multi" to "multi" and "multi2", both at the same position,
/// and expands "triplemulti" to "triplemulti", "multi3", and "multi2".
/// </summary>
private class MultiAnalyzer : Analyzer
{
protected internal override TokenStreamComponents CreateComponents(string fieldName, System.IO.TextReader reader)
{
Tokenizer result = new MockTokenizer(reader, MockTokenizer.WHITESPACE, true);
return new TokenStreamComponents(result, new TestFilter(result));
}
}
private sealed class TestFilter : TokenFilter
{
private string prevType;
private int prevStartOffset;
private int prevEndOffset;
private readonly ICharTermAttribute termAtt;
private readonly IPositionIncrementAttribute posIncrAtt;
private readonly IOffsetAttribute offsetAtt;
private readonly ITypeAttribute typeAtt;
public TestFilter(TokenStream @in)
: base(@in)
{
termAtt = AddAttribute<ICharTermAttribute>();
posIncrAtt = AddAttribute<IPositionIncrementAttribute>();
offsetAtt = AddAttribute<IOffsetAttribute>();
typeAtt = AddAttribute<ITypeAttribute>();
}
public override sealed bool IncrementToken()
{
if (multiToken > 0)
{
termAtt.SetEmpty().Append("multi" + (multiToken + 1));
offsetAtt.SetOffset(prevStartOffset, prevEndOffset);
typeAtt.Type = (prevType);
posIncrAtt.PositionIncrement = (0);
multiToken--;
return true;
}
else
{
bool next = m_input.IncrementToken();
if (!next)
{
return false;
}
prevType = typeAtt.Type;
prevStartOffset = offsetAtt.StartOffset;
prevEndOffset = offsetAtt.EndOffset;
string text = termAtt.toString();
if (text.Equals("triplemulti", StringComparison.Ordinal))
{
multiToken = 2;
return true;
}
else if (text.Equals("multi", StringComparison.Ordinal))
{
multiToken = 1;
return true;
}
else
{
return true;
}
}
}
public override void Reset()
{
base.Reset();
this.prevType = null;
this.prevStartOffset = 0;
this.prevEndOffset = 0;
}
}
/// <summary>
/// Analyzes "the quick brown" as: quick(incr=2) brown(incr=1).
/// Does not work correctly for input other than "the quick brown ...".
/// </summary>
private class PosIncrementAnalyzer : Analyzer
{
protected internal override TokenStreamComponents CreateComponents(string fieldName, System.IO.TextReader reader)
{
Tokenizer result = new MockTokenizer(reader, MockTokenizer.WHITESPACE, true);
return new TokenStreamComponents(result, new TestPosIncrementFilter(result));
}
}
private sealed class TestPosIncrementFilter : TokenFilter
{
ICharTermAttribute termAtt;
IPositionIncrementAttribute posIncrAtt;
public TestPosIncrementFilter(TokenStream @in)
: base(@in)
{
termAtt = AddAttribute<ICharTermAttribute>();
posIncrAtt = AddAttribute<IPositionIncrementAttribute>();
}
public override sealed bool IncrementToken()
{
while (m_input.IncrementToken())
{
if (termAtt.toString().Equals("the", StringComparison.Ordinal))
{
// stopword, do nothing
}
else if (termAtt.toString().Equals("quick", StringComparison.Ordinal))
{
posIncrAtt.PositionIncrement = (2);
return true;
}
else
{
posIncrAtt.PositionIncrement = (1);
return true;
}
}
return false;
}
}
/// <summary>
/// a very simple subclass of QueryParser
/// </summary>
private sealed class DumbQueryParser : QueryParser
{
public DumbQueryParser(string f, Analyzer a)
: base(TEST_VERSION_CURRENT, f, a)
{
}
// expose super's version
public Query GetSuperFieldQuery(string f, string t, bool quoted)
{
return base.GetFieldQuery(f, t, quoted);
}
// wrap super's version
protected internal override Query GetFieldQuery(string field, string queryText, bool quoted)
{
return new DumbQueryWrapper(GetSuperFieldQuery(field, queryText, quoted));
}
}
/// <summary>
/// A very simple wrapper to prevent instanceof checks but uses
/// the toString of the query it wraps.
/// </summary>
private sealed class DumbQueryWrapper : Query
{
private Query q;
public DumbQueryWrapper(Query q)
{
this.q = q;
}
public override string ToString(string field)
{
return q.ToString(field);
}
}
}
}