org.apache.solr.response.transform.ChildDocTransformerFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.solr.response.transform.ChildDocTransformerFactory.java

Source

/*
 * 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.
 */
package org.apache.solr.response.transform;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.search.join.FixedBitSetCachingWrapperFilter;
import org.apache.lucene.search.join.ToChildBlockJoinQuery;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.UnicodeUtil;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.ResponseWriterUtil;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.search.DocIterator;
import org.apache.solr.search.DocList;
import org.apache.solr.search.QParser;
import org.apache.solr.search.SyntaxError;

/**
 *
 * @since solr 4.9
 *
 * This transformer returns all descendants of each parent document in a flat list nested inside the parent document.
 *
 *
 * The "parentFilter" parameter is mandatory.
 * Optionally you can provide a "childFilter" param to filter out which child documents should be returned and a
 * "limit" param which provides an option to specify the number of child documents
 * to be returned per parent document. By default it's set to 10.
 *
 * Examples -
 * [child parentFilter="fieldName:fieldValue"]
 * [child parentFilter="fieldName:fieldValue" childFilter="fieldName:fieldValue"]
 * [child parentFilter="fieldName:fieldValue" childFilter="fieldName:fieldValue" limit=20]
 */
public class ChildDocTransformerFactory extends TransformerFactory {

    @Override
    public DocTransformer create(String field, SolrParams params, SolrQueryRequest req) {
        SchemaField uniqueKeyField = req.getSchema().getUniqueKeyField();
        if (uniqueKeyField == null) {
            throw new SolrException(ErrorCode.BAD_REQUEST,
                    " ChildDocTransformer requires the schema to have a uniqueKeyField.");
        }

        String parentFilter = params.get("parentFilter");
        if (parentFilter == null) {
            throw new SolrException(ErrorCode.BAD_REQUEST,
                    "Parent filter should be sent as parentFilter=filterCondition");
        }

        String childFilter = params.get("childFilter");
        int limit = params.getInt("limit", 10);

        Filter parentsFilter = null;
        try {
            Query parentFilterQuery = QParser.getParser(parentFilter, null, req).getQuery();
            parentsFilter = new FixedBitSetCachingWrapperFilter(new QueryWrapperFilter(parentFilterQuery));
        } catch (SyntaxError syntaxError) {
            throw new SolrException(ErrorCode.BAD_REQUEST, "Failed to create correct parent filter query");
        }

        Query childFilterQuery = null;
        if (childFilter != null) {
            try {
                childFilterQuery = QParser.getParser(childFilter, null, req).getQuery();
            } catch (SyntaxError syntaxError) {
                throw new SolrException(ErrorCode.BAD_REQUEST, "Failed to create correct child filter query");
            }
        }

        return new ChildDocTransformer(field, parentsFilter, uniqueKeyField, req.getSchema(), childFilterQuery,
                limit);
    }
}

class ChildDocTransformer extends TransformerWithContext {
    private final String name;
    private final SchemaField idField;
    private final IndexSchema schema;
    private Filter parentsFilter;
    private Query childFilterQuery;
    private int limit;

    public ChildDocTransformer(String name, final Filter parentsFilter, final SchemaField idField,
            IndexSchema schema, final Query childFilterQuery, int limit) {
        this.name = name;
        this.idField = idField;
        this.schema = schema;
        this.parentsFilter = parentsFilter;
        this.childFilterQuery = childFilterQuery;
        this.limit = limit;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void transform(SolrDocument doc, int docid) {

        FieldType idFt = idField.getType();
        Object parentIdField = doc.getFirstValue(idField.getName());

        String parentIdExt = parentIdField instanceof IndexableField
                ? idFt.toExternal((IndexableField) parentIdField)
                : parentIdField.toString();

        try {
            Query parentQuery = idFt.getFieldQuery(null, idField, parentIdExt);
            Query query = new ToChildBlockJoinQuery(parentQuery, parentsFilter, false);
            DocList children = context.searcher.getDocList(query, childFilterQuery, new Sort(), 0, limit);
            if (children.matches() > 0) {
                DocIterator i = children.iterator();
                while (i.hasNext()) {
                    Integer childDocNum = i.next();
                    Document childDoc = context.searcher.doc(childDocNum);
                    SolrDocument solrChildDoc = ResponseWriterUtil.toSolrDocument(childDoc, schema);

                    // TODO: future enhancement...
                    // support an fl local param in the transformer, which is used to build
                    // a private ReturnFields instance that we use to prune unwanted field 
                    // names from solrChildDoc
                    doc.addChildDocument(solrChildDoc);
                }
            }

        } catch (IOException e) {
            doc.put(name, "Could not fetch child Documents");
        }
    }
}