Как заставить QueryParser в Lucene обрабатывать числовые диапазоны? - PullRequest
6 голосов
/ 17 февраля 2011
new QueryParser(.... ).parse (somequery);

работает только для строковых индексированных полей.Скажем, у меня есть поле с именем count, где count является целочисленным полем (при индексации поля я рассмотрел тип данных)

new QueryParser(....).parse("count:[1 TO 10]");

Вышеуказанное не работает.Вместо этого, если я использовал "NumericRangeQuery.newIntRange" , который работает.Но мне нужно только одно выше ...

Ответы [ 5 ]

5 голосов
/ 04 марта 2015

Была такая же проблема и решена, поэтому здесь я делюсь своим решением:

Чтобы создать собственный анализатор запросов, который будет анализировать следующий запрос "INTFIELD_NAME: 1203" или "INTFIELD_NAME: [1 - 10]"и обработав поле INTFIELD_NAME как Intfield, я переопределил newTermQuery следующим образом:

public class CustomQueryParser extends QueryParser {

public CustomQueryParser(String f, Analyzer a) {
    super(f, a);
}

protected Query newRangeQuery(String field, String part1, String part2, boolean startInclusive,
    boolean endInclusive) {

    if (INTFIELD_NAME.equals(field)) {
    return NumericRangeQuery.newIntRange(field, Integer.parseInt(part1), Integer.parseInt(part2),
        startInclusive, endInclusive);
    }
    return (TermRangeQuery) super.newRangeQuery(field, part1, part2, startInclusive, endInclusive);
}


protected Query newTermQuery(Term term) {
    if (INTFIELD_NAME.equals(term.field())) {

    BytesRefBuilder byteRefBuilder = new BytesRefBuilder();
    NumericUtils.intToPrefixCoded(Integer.parseInt(term.text()), 0, byteRefBuilder);
    TermQuery tq = new TermQuery(new Term(term.field(), byteRefBuilder.get()));

    return tq;
    } 
    return super.newTermQuery(term);

}
}

Я взял код, указанный в этой теме, из http://www.mail -archive.com / search? l =java-user@lucene.apache.org&q=subject:% 22Re% 3A + Как + сделать + вы + правильно + использовать + NumericField% 22 & o = newest & f = 1 и сделал 3 изменения:

  • переписал newRangeQuery немного лучше

  • заменен в методе newTermQuery NumericUtils.intToPrefixCoded(Integer.parseInt(term.text()),NumericUtils.PRECISION_STEP_DEFAULT)));

    на NumericUtils.intToPrefixCoded(Integer.parseInt(term.text()), 0, byteRefBuilder);

когда я впервые использовал этот метод в фильтре для того же числового поля, я поставил 0, поскольку нашел его в качестве значения по умолчанию в классе lucene, и он просто работал.

  • заменено на newTermQuery

    TermQuery tq = new TermQuery(new Term(field,

на TermQuery tq = new TermQuery(new Term(term.field(),

с использованием "поля"g, потому что если в вашем запросе есть несколько предложений (FIELD: text OR INTFIELD: 100), он принимает поле первого или предыдущего предложения.

2 голосов
/ 16 августа 2011

Вам нужно наследовать от QueryParser и переопределять GetRangeQuery(string field, ...). Если field является одним из ваших числовых имен полей, верните экземпляр NumericRangeQuery, в противном случае верните base.GetRangeQuery(...).

В этой теме есть пример такой реализации: http://www.mail-archive.com/java-user@lucene.apache.org/msg29062.html

1 голос
/ 22 сентября 2016

В Lucene 6 защищенный метод QueryParser#getRangeQuery все еще существует со списком аргументов (String fieldName, String low, String high, boolean startInclusive, boolean endInclusive), и его можно переопределить, чтобы интерпретировать диапазон как числовой диапазон, действительно, до тех пор, пока эта информацияиндексируется с использованием одного из новых полей Point.

При индексации вашего поля:

document.add(new FloatPoint("_point_count", value)); // index for efficient range based retrieval
document.add(new StoredField("count", value)); // if you need to store the value itself

В своем анализаторе пользовательских запросов (расширение queryparser.classic.QueryParser) переопределите метод примерно так::

@Override
protected Query getRangeQuery(String field, String low, String high, boolean startInclusive, boolean endInclusive) throws ParseException
{
    if («isNumericField»(field)) // context dependent
    {
        final String pointField = "_point_" + field;
        return FloatPoint.newRangeQuery(pointField,
                Float.parseFloat(low),
                Float.parseFloat(high));
    }

    return super.getRangeQuery(field, low, high, startInclusive, endInclusive);
}
1 голос
/ 18 февраля 2011

QueryParser не будет создавать NumericRangeQuery, поскольку у него нет возможности узнать, было ли поле проиндексировано с помощью NumericField. Просто расширьте QueryParser для обработки этого случая.

0 голосов
/ 28 июня 2017

Я адаптировал ответ Джеремиса для C # и Lucene.Net 3.0.3. Мне также нужен был тип double вместо int. Это мой код:

using System.Globalization;
using Lucene.Net.Analysis;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using Lucene.Net.Util;
using Version = Lucene.Net.Util.Version;

namespace SearchServer.SearchEngine
{
    internal class SearchQueryParser : QueryParser
    {
        public SearchQueryParser(Analyzer analyzer)
            : base(Version.LUCENE_30, null, analyzer)
        {
        }

        private const NumberStyles DblNumberStyles = NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite | NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint;

        protected override Query NewRangeQuery(string field, string part1, string part2, bool inclusive)
        {
            if (field == "p")
            {
                double part1Dbl;
                if (!double.TryParse(part1, DblNumberStyles, CultureInfo.InvariantCulture, out part1Dbl))
                    throw new ParseException($"Error parsing value {part1} for field {field} as double.");
                double part2Dbl;
                if (!double.TryParse(part2, DblNumberStyles, CultureInfo.InvariantCulture, out part2Dbl))
                    throw new ParseException($"Error parsing value {part2} for field {field} as double.");
                return NumericRangeQuery.NewDoubleRange(field, part1Dbl, part2Dbl, inclusive, inclusive);
            }
            return base.NewRangeQuery(field, part1, part2, inclusive);
        }

        protected override Query NewTermQuery(Term term)
        {
            if (term.Field == "p")
            {
                double dblParsed;
                if (!double.TryParse(term.Text, DblNumberStyles, CultureInfo.InvariantCulture, out dblParsed))
                    throw new ParseException($"Error parsing value {term.Text} for field {term.Field} as double.");
                return new TermQuery(new Term(term.Field, NumericUtils.DoubleToPrefixCoded(dblParsed)));
            }
            return base.NewTermQuery(term);
        }
    }
}

Я улучшил свой код, чтобы также разрешать запросы, такие как больше и меньше, чем при передаче звездочки. Например. p:[* TO 5]

...
    double? part1Dbl = null;
    double tmpDbl;
    if (part1 != "*")
    {
        if (!double.TryParse(part1, DblNumberStyles, CultureInfo.InvariantCulture, out tmpDbl))
            throw new ParseException($"Error parsing value {part1} for field {field} as double.");
        part1Dbl = tmpDbl;
    }
    double? part2Dbl = null;
    if (part2 != "*")
    {
        if (!double.TryParse(part2, DblNumberStyles, CultureInfo.InvariantCulture, out tmpDbl))
            throw new ParseException($"Error parsing value {part2} for field {field} as double.");
        part2Dbl = tmpDbl;
    }
    return NumericRangeQuery.NewDoubleRange(field, part1Dbl, part2Dbl, inclusive, inclusive);
...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...