| using Lucene.Net.Documents; |
| using Lucene.Net.Expressions.JS; |
| using Lucene.Net.Index; |
| using Lucene.Net.Search; |
| using Lucene.Net.Store; |
| using Lucene.Net.Util; |
| using NUnit.Framework; |
| using System; |
| using System.Text; |
| using Assert = Lucene.Net.TestFramework.Assert; |
| |
| namespace Lucene.Net.Expressions |
| { |
| /* |
| * 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. |
| */ |
| |
| /// <summary>simple demo of using expressions</summary> |
| [SuppressCodecs("Lucene3x")] |
| public class TestDemoExpressions : LuceneTestCase |
| { |
| internal IndexSearcher searcher; |
| |
| internal DirectoryReader reader; |
| |
| internal Directory dir; |
| |
| [SetUp] |
| public override void SetUp() |
| { |
| base.SetUp(); |
| dir = NewDirectory(); |
| var iw = new RandomIndexWriter( |
| #if FEATURE_INSTANCE_TESTDATA_INITIALIZATION |
| this, |
| #endif |
| Random, dir); |
| var doc = new Document |
| { |
| NewStringField("id", "1", Field.Store.YES), |
| NewTextField("body", "some contents and more contents", Field.Store.NO), |
| new NumericDocValuesField("popularity", 5), |
| new DoubleField("latitude", 40.759011, Field.Store.NO), |
| new DoubleField("longitude", -73.9844722, Field.Store.NO) |
| }; |
| iw.AddDocument(doc); |
| doc = new Document |
| { |
| NewStringField("id", "2", Field.Store.YES), |
| NewTextField("body", "another document with different contents", Field.Store |
| .NO), |
| new NumericDocValuesField("popularity", 20), |
| new DoubleField("latitude", 40.718266, Field.Store.NO), |
| new DoubleField("longitude", -74.007819, Field.Store.NO) |
| }; |
| iw.AddDocument(doc); |
| doc = new Document |
| { |
| NewStringField("id", "3", Field.Store.YES), |
| NewTextField("body", "crappy contents", Field.Store.NO), |
| new NumericDocValuesField("popularity", 2), |
| new DoubleField("latitude", 40.7051157, Field.Store.NO), |
| new DoubleField("longitude", -74.0088305, Field.Store.NO) |
| }; |
| iw.AddDocument(doc); |
| reader = iw.GetReader(); |
| searcher = new IndexSearcher(reader); |
| iw.Dispose(); |
| } |
| |
| [TearDown] |
| public override void TearDown() |
| { |
| reader.Dispose(); |
| dir.Dispose(); |
| base.TearDown(); |
| } |
| |
| /// <summary>an example of how to rank by an expression</summary> |
| [Test] |
| public virtual void Test() |
| { |
| // compile an expression: |
| var expr = JavascriptCompiler.Compile("sqrt(_score) + ln(popularity)"); |
| // we use SimpleBindings: which just maps variables to SortField instances |
| SimpleBindings bindings = new SimpleBindings(); |
| bindings.Add(new SortField("_score", SortFieldType.SCORE)); |
| bindings.Add(new SortField("popularity", SortFieldType.INT32)); |
| // create a sort field and sort by it (reverse order) |
| Sort sort = new Sort(expr.GetSortField(bindings, true)); |
| Query query = new TermQuery(new Term("body", "contents")); |
| searcher.Search(query, null, 3, sort); |
| } |
| |
| /// <summary>tests the returned sort values are correct</summary> |
| [Test] |
| public virtual void TestSortValues() |
| { |
| var expr = JavascriptCompiler.Compile("sqrt(_score)"); |
| SimpleBindings bindings = new SimpleBindings(); |
| bindings.Add(new SortField("_score", SortFieldType.SCORE)); |
| Sort sort = new Sort(expr.GetSortField(bindings, true)); |
| Query query = new TermQuery(new Term("body", "contents")); |
| TopFieldDocs td = searcher.Search(query, null, 3, sort, true, true); |
| for (int i = 0; i < 3; i++) |
| { |
| FieldDoc d = (FieldDoc)td.ScoreDocs[i]; |
| float expected = (float)Math.Sqrt(d.Score); |
| float actual = (float)((double)d.Fields[0]); |
| Assert.AreEqual(expected, actual, CheckHits.ExplainToleranceDelta(expected, actual)); |
| } |
| } |
| |
| /// <summary>tests same binding used more than once in an expression</summary> |
| [Test] |
| public virtual void TestTwoOfSameBinding() |
| { |
| var expr = JavascriptCompiler.Compile("_score + _score"); |
| SimpleBindings bindings = new SimpleBindings(); |
| bindings.Add(new SortField("_score", SortFieldType.SCORE)); |
| Sort sort = new Sort(expr.GetSortField(bindings, true)); |
| Query query = new TermQuery(new Term("body", "contents")); |
| TopFieldDocs td = searcher.Search(query, null, 3, sort, true, true); |
| for (int i = 0; i < 3; i++) |
| { |
| FieldDoc d = (FieldDoc)td.ScoreDocs[i]; |
| float expected = 2 * d.Score; |
| float actual = (float)((double)d.Fields[0]); |
| Assert.AreEqual(expected, actual, CheckHits.ExplainToleranceDelta |
| (expected, actual)); |
| } |
| } |
| |
| /// <summary>tests expression referring to another expression</summary> |
| [Test] |
| public virtual void TestExpressionRefersToExpression() |
| { |
| var expr1 = JavascriptCompiler.Compile("_score"); |
| var expr2 = JavascriptCompiler.Compile("2*expr1"); |
| var bindings = new SimpleBindings(); |
| bindings.Add(new SortField("_score", SortFieldType.SCORE)); |
| bindings.Add("expr1", expr1); |
| Sort sort = new Sort(expr2.GetSortField(bindings, true)); |
| Query query = new TermQuery(new Term("body", "contents")); |
| TopFieldDocs td = searcher.Search(query, null, 3, sort, true, true); |
| for (int i = 0; i < 3; i++) |
| { |
| FieldDoc d = (FieldDoc)td.ScoreDocs[i]; |
| float expected = 2 * d.Score; |
| float actual = (float)((double)d.Fields[0]); |
| Assert.AreEqual(expected, actual, CheckHits.ExplainToleranceDelta |
| (expected, actual)); |
| } |
| } |
| |
| /// <summary>tests huge amounts of variables in the expression</summary> |
| [Test] |
| public virtual void TestLotsOfBindings() |
| { |
| DoTestLotsOfBindings(byte.MaxValue - 1); |
| DoTestLotsOfBindings(byte.MaxValue); |
| DoTestLotsOfBindings(byte.MaxValue + 1); |
| } |
| |
| // TODO: ideally we'd test > Short.MAX_VALUE too, but compilation is currently recursive. |
| // so if we want to test such huge expressions, we need to instead change parser to use an explicit Stack |
| /// <exception cref="Exception"></exception> |
| private void DoTestLotsOfBindings(int n) |
| { |
| SimpleBindings bindings = new SimpleBindings(); |
| StringBuilder sb = new StringBuilder(); |
| for (int i = 0; i < n; i++) |
| { |
| if (i > 0) |
| { |
| sb.Append("+"); |
| } |
| sb.Append("x" + i); |
| bindings.Add(new SortField("x" + i, SortFieldType.SCORE)); |
| } |
| var expr = JavascriptCompiler.Compile(sb.ToString()); |
| var sort = new Sort(expr.GetSortField(bindings, true)); |
| Query query = new TermQuery(new Term("body", "contents")); |
| TopFieldDocs td = searcher.Search(query, null, 3, sort, true, true); |
| for (int i_1 = 0; i_1 < 3; i_1++) |
| { |
| FieldDoc d = (FieldDoc)td.ScoreDocs[i_1]; |
| float expected = n * d.Score; |
| float actual = (float)((double)d.Fields[0]); |
| Assert.AreEqual(expected, actual, CheckHits.ExplainToleranceDelta(expected, actual)); |
| } |
| } |
| |
| [Test] |
| public virtual void TestDistanceSort() |
| { |
| var distance = JavascriptCompiler.Compile("haversin(40.7143528,-74.0059731,latitude,longitude)"); |
| SimpleBindings bindings = new SimpleBindings(); |
| bindings.Add(new SortField("latitude", SortFieldType.DOUBLE)); |
| bindings.Add(new SortField("longitude", SortFieldType.DOUBLE)); |
| Sort sort = new Sort(distance.GetSortField(bindings, false)); |
| TopFieldDocs td = searcher.Search(new MatchAllDocsQuery(), null, 3, sort); |
| FieldDoc d = (FieldDoc)td.ScoreDocs[0]; |
| Assert.AreEqual(0.4619D, (double)d.Fields[0], 1E-4); |
| d = (FieldDoc)td.ScoreDocs[1]; |
| Assert.AreEqual(1.0546D, (double)d.Fields[0], 1E-4); |
| d = (FieldDoc)td.ScoreDocs[2]; |
| Assert.AreEqual(5.2842D, (double)d.Fields[0], 1E-4); |
| } |
| } |
| } |