blob: 9d7f6e6530b667f119dc5b90267c24f37e1b403b [file] [log] [blame]
// Lucene version compatibility level 4.8.1
using Lucene.Net.Analysis;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Join;
using Lucene.Net.Search;
using Lucene.Net.Store;
using Lucene.Net.Util;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Text;
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 const 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", "*"))));
}
[Test]
public void TestNextDocValidationForToParentBjq()
{
Query parentQueryWithRandomChild = CreateChildrenQueryWithOneParent(GetRandomChildNumber(0));
var blockJoinQuery = new ToParentBlockJoinQuery(parentQueryWithRandomChild, parentsFilter, ScoreMode.None);
var ex = Assert.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, Occur.MUST));
conjunctionQuery.Add(new BooleanClause(blockJoinQuery, Occur.MUST));
var ex = Assert.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 = Assert.Throws<InvalidOperationException>(() => indexSearcher.Search(blockJoinQuery, 1));
StringAssert.Contains(ToChildBlockJoinQuery.INVALID_QUERY_MESSAGE, 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, Occur.MUST));
conjunctionQuery.Add(new BooleanClause(blockJoinQuery, Occur.MUST));
var ex = Assert.Throws<InvalidOperationException>(() => indexSearcher.Search(conjunctionQuery, 1));
StringAssert.Contains(ToChildBlockJoinQuery.INVALID_QUERY_MESSAGE, ex.Message);
}
[TearDown]
public override void TearDown()
{
indexReader.Dispose();
directory.Dispose();
}
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(GetRandomParentId())));
BooleanQuery childrenQueryWithRandomParent = new BooleanQuery();
childrenQueryWithRandomParent.Add(new BooleanClause(childQuery, Occur.SHOULD));
childrenQueryWithRandomParent.Add(new BooleanClause(randomParentQuery, Occur.SHOULD));
return childrenQueryWithRandomParent;
}
private static Query CreateParentsQueryWithOneChild(int randomChildNumber)
{
BooleanQuery childQueryWithRandomParent = new BooleanQuery();
Query parentsQuery = new TermQuery(new Term("parent", CreateFieldValue(GetRandomParentNumber())));
childQueryWithRandomParent.Add(new BooleanClause(parentsQuery, Occur.SHOULD));
childQueryWithRandomParent.Add(new BooleanClause(RandomChildQuery(randomChildNumber), Occur.SHOULD));
return childQueryWithRandomParent;
}
private static int GetRandomParentId() => Random.Next(AMOUNT_OF_PARENT_DOCS*AMOUNT_OF_SEGMENTS);
private static int GetRandomParentNumber() => Random.Next(AMOUNT_OF_PARENT_DOCS);
private static Query RandomChildQuery(int randomChildNumber)
{
return new TermQuery(new Term("id", CreateFieldValue(GetRandomParentId(), randomChildNumber)));
}
private static int GetRandomChildNumber(int notLessThan)
{
return notLessThan + Random.Next(AMOUNT_OF_CHILD_DOCS - notLessThan);
}
}
}