All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.solr.response.transform.GeoTransformerFactory Maven / Gradle / Ivy

There is a newer version: 9.7.0
Show newest version
/*
 * 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.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.ReaderUtil;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.spatial.ShapeValues;
import org.apache.lucene.spatial.ShapeValuesSource;
import org.apache.lucene.spatial.SpatialStrategy;
import org.apache.lucene.spatial.composite.CompositeSpatialStrategy;
import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
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.JSONResponseWriter;
import org.apache.solr.response.JacksonJsonWriter;
import org.apache.solr.response.QueryResponseWriter;
import org.apache.solr.schema.AbstractSpatialFieldType;
import org.apache.solr.schema.SchemaField;
import org.locationtech.spatial4j.io.GeoJSONWriter;
import org.locationtech.spatial4j.io.ShapeWriter;
import org.locationtech.spatial4j.io.SupportedFormats;
import org.locationtech.spatial4j.shape.Shape;

/**
 * This DocumentTransformer will write a {@link Shape} to the SolrDocument using the requested
 * format. Supported formats include:
 *
 * 
    *
  • GeoJSON *
  • WKT *
  • Polyshape *
* * For more information see: spatial4j/FORMATS.md * *

The shape is either read from a stored field, or a ValueSource. * *

This transformer is useful when: * *

    *
  • You want to return a format different than the stored encoding (WKT vs GeoJSON) *
  • The {@link Shape} is stored in a {@link ValueSource}, not a stored field *
  • the value is not stored in a format the output understands (ie, raw GeoJSON) *
*/ public class GeoTransformerFactory extends TransformerFactory implements TransformerFactory.FieldRenamer { @Override public DocTransformer create(String display, SolrParams params, SolrQueryRequest req) { throw new UnsupportedOperationException(); } @Override public DocTransformer create( String display, SolrParams params, SolrQueryRequest req, Map renamedFields, Set reqFieldNames) { String fname = params.get("f", display); if (fname.startsWith("[") && fname.endsWith("]")) { fname = display.substring(1, display.length() - 1); } SchemaField sf = req.getSchema().getFieldOrNull(fname); if (sf == null) { throw new SolrException( ErrorCode.BAD_REQUEST, this.getClass().getSimpleName() + " using unknown field: " + fname); } if (!(sf.getType() instanceof AbstractSpatialFieldType)) { throw new SolrException( ErrorCode.BAD_REQUEST, "GeoTransformer requested non-spatial field: " + fname + " (" + sf.getType().getClass().getSimpleName() + ")"); } final GeoFieldUpdater updater = new GeoFieldUpdater(); updater.field = fname; updater.display = display; updater.display_error = display + "_error"; final ShapeValuesSource shapes; AbstractSpatialFieldType sdv = (AbstractSpatialFieldType) sf.getType(); SpatialStrategy strategy = sdv.getStrategy(fname); if (strategy instanceof CompositeSpatialStrategy) { shapes = ((CompositeSpatialStrategy) strategy).getGeometryStrategy().makeShapeValueSource(); } else if (strategy instanceof SerializedDVStrategy) { shapes = ((SerializedDVStrategy) strategy).makeShapeValueSource(); } else shapes = null; String writerName = params.get("w", "GeoJSON"); updater.formats = strategy.getSpatialContext().getFormats(); updater.writer = updater.formats.getWriter(writerName); if (updater.writer == null) { StringBuilder str = new StringBuilder(); str.append("Unknown Spatial Writer: ").append(writerName); str.append(" ["); for (ShapeWriter w : updater.formats.getWriters()) { str.append(w.getFormatName()).append(' '); } str.append("]"); throw new SolrException(ErrorCode.BAD_REQUEST, str.toString()); } QueryResponseWriter qw = req.getCore().getQueryResponseWriter(req); updater.isJSON = (qw.getClass() == JSONResponseWriter.class || qw.getClass() == JacksonJsonWriter.class) && (updater.writer instanceof GeoJSONWriter); // Using ValueSource if (shapes != null) { return new GeoDocTransformer(updater) { @Override public void transform(SolrDocument doc, int docid) throws IOException { int leafOrd = ReaderUtil.subIndex(docid, context.getSearcher().getTopReaderContext().leaves()); LeafReaderContext ctx = context.getSearcher().getTopReaderContext().leaves().get(leafOrd); ShapeValues values = shapes.getValues(ctx); int segmentDoc = docid - ctx.docBase; if (values.advanceExact(segmentDoc)) { updater.setValue(doc, values.value()); } } }; } // if source has been renamed, update reference updater.field = renamedFields.getOrDefault(updater.field, updater.field); // don't remove fields that were explicitly requested by others final boolean copy = reqFieldNames != null && reqFieldNames.contains(updater.field); if (!copy) { renamedFields.put(updater.field, updater.display); } // Using the raw stored values return new GeoDocTransformer(updater) { @Override public void transform(SolrDocument doc, int docid) throws IOException { Object val = copy ? doc.get(updater.field) : doc.remove(updater.field); if (val != null) { updater.setValue(doc, val); } } @Override public String[] getExtraRequestFields() { return new String[] {updater.field}; } }; } private abstract static class GeoDocTransformer extends DocTransformer { private final GeoFieldUpdater updater; private GeoDocTransformer(GeoFieldUpdater updater) { this.updater = updater; } @Override public String getName() { return updater.display; } @Override public Collection getRawFields() { return updater.isJSON ? Collections.singleton(updater.display) : Collections.emptySet(); } } private static class GeoFieldUpdater { String field; String display; String display_error; boolean isJSON; ShapeWriter writer; SupportedFormats formats; void addShape(SolrDocument doc, Shape shape) { doc.addField(display, writer.toString(shape)); } void setValue(SolrDocument doc, Object val) { doc.remove(display); if (val != null) { if (val instanceof Iterable) { Iterator iter = ((Iterable) val).iterator(); while (iter.hasNext()) { addValue(doc, iter.next()); } } else { addValue(doc, val); } } } void addValue(SolrDocument doc, Object val) { if (val == null) { return; } if (val instanceof Shape) { addShape(doc, (Shape) val); } // Don't explode on 'InvalidShpae' else if (val instanceof Exception) { doc.setField(display_error, ((Exception) val).toString()); } else { // Use the stored value if (val instanceof IndexableField) { val = ((IndexableField) val).stringValue(); } try { addShape(doc, formats.read(val.toString())); } catch (Exception ex) { doc.setField(display_error, ex.toString()); } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy