blob: 3eeaebba7202d3758e2b3d1c4028024170507e08 [file] [log] [blame]
using System;
using System.Collections.Generic;
using System.Text;
using Lucene.Net.Analysis;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Join;
using Lucene.Net.Randomized.Generators;
using Lucene.Net.Search;
using Lucene.Net.Store;
using Lucene.Net.Support;
using Lucene.Net.Util;
using NUnit.Framework;
namespace Lucene.Net.Tests.Join
{
/*
* 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.
*/
public class TestBlockJoinValidation : LuceneTestCase
{
public const int AMOUNT_OF_SEGMENTS = 5;
public const int AMOUNT_OF_PARENT_DOCS = 10;
public const int AMOUNT_OF_CHILD_DOCS = 5;
public static readonly int AMOUNT_OF_DOCS_IN_SEGMENT = AMOUNT_OF_PARENT_DOCS + AMOUNT_OF_PARENT_DOCS * AMOUNT_OF_CHILD_DOCS;
private Directory Directory;
private IndexReader IndexReader;
private IndexSearcher IndexSearcher;
private Filter ParentsFilter;
[SetUp]
public override void SetUp()
{
Directory = NewDirectory();
IndexWriterConfig config = new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random()));
IndexWriter indexWriter = new IndexWriter(Directory, config);
for (int i = 0; i < AMOUNT_OF_SEGMENTS; i++)
{
IList<Document> segmentDocs = CreateDocsForSegment(i);
indexWriter.AddDocuments(segmentDocs);
indexWriter.Commit();
}
IndexReader = DirectoryReader.Open(indexWriter, Random().NextBoolean());
indexWriter.Dispose();
IndexSearcher = new IndexSearcher(IndexReader);
ParentsFilter = new FixedBitSetCachingWrapperFilter(new QueryWrapperFilter(new WildcardQuery(new Term("parent", "*"))));
}
[TearDown]
public override void TearDown()
{
IndexReader.Dispose();
Directory.Dispose();
}
[Test]
public void TestNextDocValidationForToParentBjq()
{
Query parentQueryWithRandomChild = CreateChildrenQueryWithOneParent(GetRandomChildNumber(0));
var blockJoinQuery = new ToParentBlockJoinQuery(parentQueryWithRandomChild, ParentsFilter, ScoreMode.None);
var ex = Throws<InvalidOperationException>(() => IndexSearcher.Search(blockJoinQuery, 1));
StringAssert.Contains("child query must only match non-parent docs", ex.Message);
}
[Test]
public void TestAdvanceValidationForToParentBjq()
{
int randomChildNumber = GetRandomChildNumber(0);
// we need to make advance method meet wrong document, so random child number
// in BJQ must be greater than child number in Boolean clause
int nextRandomChildNumber = GetRandomChildNumber(randomChildNumber);
Query parentQueryWithRandomChild = CreateChildrenQueryWithOneParent(nextRandomChildNumber);
ToParentBlockJoinQuery blockJoinQuery = new ToParentBlockJoinQuery(parentQueryWithRandomChild, ParentsFilter, ScoreMode.None);
// advance() method is used by ConjunctionScorer, so we need to create Boolean conjunction query
BooleanQuery conjunctionQuery = new BooleanQuery();
WildcardQuery childQuery = new WildcardQuery(new Term("child", CreateFieldValue(randomChildNumber)));
conjunctionQuery.Add(new BooleanClause(childQuery, BooleanClause.Occur.MUST));
conjunctionQuery.Add(new BooleanClause(blockJoinQuery, BooleanClause.Occur.MUST));
var ex = Throws<InvalidOperationException>(() => IndexSearcher.Search(conjunctionQuery, 1));
StringAssert.Contains("child query must only match non-parent docs", ex.Message);
}
[Test]
public void TestNextDocValidationForToChildBjq()
{
Query parentQueryWithRandomChild = CreateParentsQueryWithOneChild(GetRandomChildNumber(0));
var blockJoinQuery = new ToChildBlockJoinQuery(parentQueryWithRandomChild, ParentsFilter, false);
var ex = Throws<InvalidOperationException>(() => IndexSearcher.Search(blockJoinQuery, 1));
StringAssert.Contains(ToChildBlockJoinQuery.InvalidQueryMessage, ex.Message);
}
[Test]
public void TestAdvanceValidationForToChildBjq()
{
int randomChildNumber = GetRandomChildNumber(0);
// we need to make advance method meet wrong document, so random child number
// in BJQ must be greater than child number in Boolean clause
int nextRandomChildNumber = GetRandomChildNumber(randomChildNumber);
Query parentQueryWithRandomChild = CreateParentsQueryWithOneChild(nextRandomChildNumber);
var blockJoinQuery = new ToChildBlockJoinQuery(parentQueryWithRandomChild, ParentsFilter, false);
// advance() method is used by ConjunctionScorer, so we need to create Boolean conjunction query
var conjunctionQuery = new BooleanQuery();
var childQuery = new WildcardQuery(new Term("child", CreateFieldValue(randomChildNumber)));
conjunctionQuery.Add(new BooleanClause(childQuery, BooleanClause.Occur.MUST));
conjunctionQuery.Add(new BooleanClause(blockJoinQuery, BooleanClause.Occur.MUST));
var ex = Throws<InvalidOperationException>(() => IndexSearcher.Search(conjunctionQuery, 1));
StringAssert.Contains(ToChildBlockJoinQuery.InvalidQueryMessage, ex.Message);
}
private IList<Document> CreateDocsForSegment(int segmentNumber)
{
IList<IList<Document>> blocks = new List<IList<Document>>(AMOUNT_OF_PARENT_DOCS);
for (int i = 0; i < AMOUNT_OF_PARENT_DOCS; i++)
{
blocks.Add(CreateParentDocWithChildren(segmentNumber, i));
}
IList<Document> result = new List<Document>(AMOUNT_OF_DOCS_IN_SEGMENT);
foreach (IList<Document> block in blocks)
{
result.AddRange(block);
}
return result;
}
private IList<Document> CreateParentDocWithChildren(int segmentNumber, int parentNumber)
{
IList<Document> result = new List<Document>(AMOUNT_OF_CHILD_DOCS + 1);
for (int i = 0; i < AMOUNT_OF_CHILD_DOCS; i++)
{
result.Add(CreateChildDoc(segmentNumber, parentNumber, i));
}
result.Add(CreateParentDoc(segmentNumber, parentNumber));
return result;
}
private Document CreateParentDoc(int segmentNumber, int parentNumber)
{
Document result = new Document();
result.Add(NewStringField("id", CreateFieldValue(segmentNumber * AMOUNT_OF_PARENT_DOCS + parentNumber), Field.Store.YES));
result.Add(NewStringField("parent", CreateFieldValue(parentNumber), Field.Store.NO));
return result;
}
private Document CreateChildDoc(int segmentNumber, int parentNumber, int childNumber)
{
Document result = new Document();
result.Add(NewStringField("id", CreateFieldValue(segmentNumber * AMOUNT_OF_PARENT_DOCS + parentNumber, childNumber), Field.Store.YES));
result.Add(NewStringField("child", CreateFieldValue(childNumber), Field.Store.NO));
return result;
}
private static string CreateFieldValue(params int[] documentNumbers)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (int documentNumber in documentNumbers)
{
if (stringBuilder.Length > 0)
{
stringBuilder.Append("_");
}
stringBuilder.Append(documentNumber);
}
return stringBuilder.ToString();
}
private static Query CreateChildrenQueryWithOneParent(int childNumber)
{
TermQuery childQuery = new TermQuery(new Term("child", CreateFieldValue(childNumber)));
Query randomParentQuery = new TermQuery(new Term("id", CreateFieldValue(RandomParentId)));
BooleanQuery childrenQueryWithRandomParent = new BooleanQuery();
childrenQueryWithRandomParent.Add(new BooleanClause(childQuery, BooleanClause.Occur.SHOULD));
childrenQueryWithRandomParent.Add(new BooleanClause(randomParentQuery, BooleanClause.Occur.SHOULD));
return childrenQueryWithRandomParent;
}
private static Query CreateParentsQueryWithOneChild(int randomChildNumber)
{
BooleanQuery childQueryWithRandomParent = new BooleanQuery();
Query parentsQuery = new TermQuery(new Term("parent", CreateFieldValue(RandomParentNumber)));
childQueryWithRandomParent.Add(new BooleanClause(parentsQuery, BooleanClause.Occur.SHOULD));
childQueryWithRandomParent.Add(new BooleanClause(RandomChildQuery(randomChildNumber), BooleanClause.Occur.SHOULD));
return childQueryWithRandomParent;
}
private static int RandomParentId
{
get { return Random().Next(AMOUNT_OF_PARENT_DOCS*AMOUNT_OF_SEGMENTS); }
}
private static int RandomParentNumber
{
get { return Random().Next(AMOUNT_OF_PARENT_DOCS); }
}
private static Query RandomChildQuery(int randomChildNumber)
{
return new TermQuery(new Term("id", CreateFieldValue(RandomParentId, randomChildNumber)));
}
private static int GetRandomChildNumber(int notLessThan)
{
return notLessThan + Random().Next(AMOUNT_OF_CHILD_DOCS - notLessThan);
}
}
}