/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch 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. */ package org.elasticsearch.index.search.child; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.join.BitSetProducer; import org.apache.lucene.util.BitDocIdSet; import org.apache.lucene.util.BitSet; import org.elasticsearch.Version; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexService; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.internal.UidFieldMapper; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.test.ESSingleNodeTestCase; import org.hamcrest.Description; import org.hamcrest.StringDescription; import java.io.IOException; import static org.hamcrest.Matchers.equalTo; public abstract class AbstractChildTestCase extends ESSingleNodeTestCase { /** * The name of the field within the child type that stores a score to use in test queries. * <p /> * Its type is {@code double}. */ protected static String CHILD_SCORE_NAME = "childScore"; static SearchContext createSearchContext(String indexName, String parentType, String childType) throws IOException { Settings settings = Settings.builder() .put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_1_6_0) .build(); IndexService indexService = createIndex(indexName, settings); MapperService mapperService = indexService.mapperService(); // Parent/child parsers require that the parent and child type to be presented in mapping // Sometimes we want a nested object field in the parent type that triggers nonNestedDocsFilter to be used mapperService.merge(parentType, new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef(parentType, "nested_field", random().nextBoolean() ? "type=nested" : "type=object").string()), MapperService.MergeReason.MAPPING_UPDATE, false); mapperService.merge(childType, new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef(childType, "_parent", "type=" + parentType, CHILD_SCORE_NAME, "type=double,doc_values=false").string()), MapperService.MergeReason.MAPPING_UPDATE, false); return createSearchContext(indexService); } static void assertBitSet(BitSet actual, BitSet expected, IndexSearcher searcher) throws IOException { assertBitSet(new BitDocIdSet(actual), new BitDocIdSet(expected), searcher); } static void assertBitSet(BitDocIdSet actual, BitDocIdSet expected, IndexSearcher searcher) throws IOException { if (!equals(expected, actual)) { Description description = new StringDescription(); description.appendText(reason(actual, expected, searcher)); description.appendText("\nExpected: "); description.appendValue(expected); description.appendText("\n got: "); description.appendValue(actual); description.appendText("\n"); throw new java.lang.AssertionError(description.toString()); } } static boolean equals(BitDocIdSet expected, BitDocIdSet actual) { if (actual == null && expected == null) { return true; } else if (actual == null || expected == null) { return false; } BitSet actualBits = actual.bits(); BitSet expectedBits = expected.bits(); if (actualBits.length() != expectedBits.length()) { return false; } for (int i = 0; i < expectedBits.length(); i++) { if (expectedBits.get(i) != actualBits.get(i)) { return false; } } return true; } static String reason(BitDocIdSet actual, BitDocIdSet expected, IndexSearcher indexSearcher) throws IOException { StringBuilder builder = new StringBuilder(); builder.append("expected cardinality:").append(expected.bits().cardinality()).append('\n'); DocIdSetIterator iterator = expected.iterator(); for (int doc = iterator.nextDoc(); doc != DocIdSetIterator.NO_MORE_DOCS; doc = iterator.nextDoc()) { builder.append("Expected doc[").append(doc).append("] with id value ").append(indexSearcher.doc(doc).get(UidFieldMapper.NAME)).append('\n'); } builder.append("actual cardinality: ").append(actual.bits().cardinality()).append('\n'); iterator = actual.iterator(); for (int doc = iterator.nextDoc(); doc != DocIdSetIterator.NO_MORE_DOCS; doc = iterator.nextDoc()) { builder.append("Actual doc[").append(doc).append("] with id value ").append(indexSearcher.doc(doc).get(UidFieldMapper.NAME)).append('\n'); } return builder.toString(); } static void assertTopDocs(TopDocs actual, TopDocs expected) { assertThat("actual.totalHits != expected.totalHits", actual.totalHits, equalTo(expected.totalHits)); assertThat("actual.getMaxScore() != expected.getMaxScore()", actual.getMaxScore(), equalTo(expected.getMaxScore())); assertThat("actual.scoreDocs.length != expected.scoreDocs.length", actual.scoreDocs.length, equalTo(actual.scoreDocs.length)); for (int i = 0; i < actual.scoreDocs.length; i++) { ScoreDoc actualHit = actual.scoreDocs[i]; ScoreDoc expectedHit = expected.scoreDocs[i]; assertThat("actualHit.doc != expectedHit.doc", actualHit.doc, equalTo(expectedHit.doc)); assertThat("actualHit.score != expectedHit.score", actualHit.score, equalTo(expectedHit.score)); } } static BitSetProducer wrapWithBitSetFilter(Query filter) { return SearchContext.current().bitsetFilterCache().getBitSetProducer(filter); } static Query parseQuery(QueryBuilder queryBuilder) throws IOException { QueryParseContext context = new QueryParseContext(new Index("test"), SearchContext.current().queryParserService()); XContentParser parser = XContentHelper.createParser(queryBuilder.buildAsBytes()); context.reset(parser); return context.parseInnerQuery(); } }