Skip to content

Commit

Permalink
HTM-1321: Make Solr schema creating more robust
Browse files Browse the repository at this point in the history
  • Loading branch information
mprins committed Nov 12, 2024
1 parent b836cc2 commit d710546
Showing 1 changed file with 115 additions and 77 deletions.
192 changes: 115 additions & 77 deletions src/main/java/org/tailormap/api/solr/SolrHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import java.util.Set;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrResponse;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.BaseHttpSolrClient;
import org.apache.solr.client.solrj.request.schema.FieldTypeDefinition;
import org.apache.solr.client.solrj.request.schema.SchemaRequest;
import org.apache.solr.client.solrj.response.QueryResponse;
Expand Down Expand Up @@ -51,23 +53,60 @@
* in a try-with-resources.
*/
public class SolrHelper implements AutoCloseable, Constants {
private static final Logger logger =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

/**
* the number of documents that are submitted per batch to the Solr service: {@value
* #SOLR_BATCH_SIZE}.
*/
public static final int SOLR_BATCH_SIZE = 1000;

private static final Logger logger =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

Check warning on line 63 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L62-L63

Added lines #L62 - L63 were not covered by tests

/** {@value #SOLR_TIMEOUT} milliseconds. */
private static final int SOLR_TIMEOUT = 7000;

private final SolrClient solrClient;

/** the Solr field type name geometry fields: {@value #SOLR_SPATIAL_FIELDNAME}. */
private static final String SOLR_SPATIAL_FIELDNAME = "tm_geometry_rpt";

private final SolrClient solrClient;

/** the Solr search field definition requests for Tailormap. */
private final Map<String, SchemaRequest.AddField> solrSearchFields =
Map.of(

Check warning on line 75 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L74-L75

Added lines #L74 - L75 were not covered by tests
SEARCH_LAYER,
new SchemaRequest.AddField(
Map.of(

Check warning on line 78 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L78

Added line #L78 was not covered by tests
"name", SEARCH_LAYER,
"type", "string",
"indexed", true,
"stored", true,
"multiValued", false,
"required", true,
"uninvertible", false)),

Check warning on line 85 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L81-L85

Added lines #L81 - L85 were not covered by tests
INDEX_GEOM_FIELD,
new SchemaRequest.AddField(
Map.of("name", INDEX_GEOM_FIELD, "type", SOLR_SPATIAL_FIELDNAME, "stored", true)),

Check warning on line 88 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L88

Added line #L88 was not covered by tests
INDEX_SEARCH_FIELD,
new SchemaRequest.AddField(
Map.of(

Check warning on line 91 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L91

Added line #L91 was not covered by tests
"name", INDEX_SEARCH_FIELD,
"type", "text_general",
"indexed", true,
"stored", true,
"multiValued", true,
"required", true,
"uninvertible", false)),

Check warning on line 98 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L94-L98

Added lines #L94 - L98 were not covered by tests
INDEX_DISPLAY_FIELD,
new SchemaRequest.AddField(
Map.of(

Check warning on line 101 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L101

Added line #L101 was not covered by tests
"name", INDEX_DISPLAY_FIELD,
"type", "text_general",
"indexed", false,
"stored", true,
"multiValued", true,
"required", true,
"uninvertible", false)));

Check warning on line 108 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L104-L108

Added lines #L104 - L108 were not covered by tests

/**
* Constructor
*
Expand Down Expand Up @@ -374,42 +413,81 @@ public SearchResponse findInIndex(
}

/**
* Programmatically create (part of) the schema if it does not exist. Only checks for the
* existence of the search layer {@link Constants#SEARCH_LAYER}.
* Close the wrapped Solr client.
*
* @throws SolrServerException if a Solr error occurs
* @throws IOException if an I/O error occurs
*/
private void createSchemaIfNotExists() throws SolrServerException, IOException {
SchemaRequest.Field fieldCheck = new SchemaRequest.Field(SEARCH_LAYER);
boolean schemaExists = true;
@Override
public void close() throws IOException {
if (null != this.solrClient) this.solrClient.close();
}

Check warning on line 423 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L423

Added line #L423 was not covered by tests

private boolean checkSchemaIfFieldExists(String fieldName) {
SchemaRequest.Field fieldCheck = new SchemaRequest.Field(fieldName);

Check warning on line 426 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L426

Added line #L426 was not covered by tests
try {
SchemaResponse.FieldResponse isField = fieldCheck.process(solrClient);
logger.debug("Field type {} exists", isField.getField());
} catch (Exception e) {
logger.debug(e.getLocalizedMessage());
logger.info("Field type {} does not exist, creating it", SEARCH_LAYER);
schemaExists = false;
logger.debug("Field {} exists", isField.getField());
return true;
} catch (SolrServerException | BaseHttpSolrClient.RemoteSolrException e) {
logger.debug(

Check warning on line 432 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L429-L432

Added lines #L429 - L432 were not covered by tests
"Field {} does not exist or could not be retrieved. Assuming it does not exist.",
fieldName);
} catch (IOException e) {
logger.error("Tried getting field: {}, but failed.", fieldName, e);
}
return false;

Check warning on line 438 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L435-L438

Added lines #L435 - L438 were not covered by tests
}

/**
* @param fieldName the name of the field to create
* @throws SolrServerException if a Solr error occurs
* @throws IOException if an I/O error occurs
*/
private void createSchemaFieldIfNotExists(String fieldName)
throws SolrServerException, IOException {
if (!checkSchemaIfFieldExists(fieldName)) {
logger.info("Creating Solr field {}.", fieldName);
SchemaRequest.AddField schemaRequest = solrSearchFields.get(fieldName);
SolrResponse response = schemaRequest.process(solrClient);
logger.debug("Field type {} created", response);
solrClient.commit();

Check warning on line 453 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L449-L453

Added lines #L449 - L453 were not covered by tests
}
}

Check warning on line 455 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L455

Added line #L455 was not covered by tests

/** Programmatically create the schema if it does not exist. */
private void createSchemaIfNotExists() {
solrSearchFields.forEach(

Check warning on line 459 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L459

Added line #L459 was not covered by tests
(key, value) -> {
try {
if (key.equals(INDEX_GEOM_FIELD)) {
createGeometryFieldTypeIfNotExists();

Check warning on line 463 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L463

Added line #L463 was not covered by tests
}
createSchemaFieldIfNotExists(key);
} catch (SolrServerException | IOException e) {
logger.error(

Check warning on line 467 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L465-L467

Added lines #L465 - L467 were not covered by tests
"Error creating schema field: {} indexing may fail. Details: {}",
key,
e.getLocalizedMessage(),

Check warning on line 470 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L470

Added line #L470 was not covered by tests
e);
}
});
}

Check warning on line 474 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L472-L474

Added lines #L472 - L474 were not covered by tests

if (schemaExists) {
private void createGeometryFieldTypeIfNotExists() throws SolrServerException, IOException {
SchemaRequest.FieldType fieldTypeCheck = new SchemaRequest.FieldType(SOLR_SPATIAL_FIELDNAME);

Check warning on line 477 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L477

Added line #L477 was not covered by tests
try {
SchemaResponse.FieldTypeResponse isFieldType = fieldTypeCheck.process(solrClient);
logger.debug("Field type {} exists", isFieldType.getFieldType());

Check warning on line 480 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L479-L480

Added lines #L479 - L480 were not covered by tests
return;
} catch (SolrServerException | BaseHttpSolrClient.RemoteSolrException e) {
logger.debug(

Check warning on line 483 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L482-L483

Added lines #L482 - L483 were not covered by tests
"Field type {} does not exist or could not be retrieved. Assuming it does not exist.",
SOLR_SPATIAL_FIELDNAME);
} catch (IOException e) {
logger.error("Tried getting field type: {}, but failed.", SOLR_SPATIAL_FIELDNAME, e);

Check warning on line 487 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L486-L487

Added lines #L486 - L487 were not covered by tests
}

logger.info("Creating Solr field type {}", SEARCH_LAYER);
SchemaRequest.AddField searchLayerSchemaRequest =
new SchemaRequest.AddField(
Map.of(
"name", SEARCH_LAYER,
"type", "string",
"indexed", true,
"stored", true,
"multiValued", false,
"required", true,
"uninvertible", false));
searchLayerSchemaRequest.process(solrClient);

logger.info("Creating Solr field type for {}", INDEX_GEOM_FIELD);
logger.info("Creating Solr field type for {}", SOLR_SPATIAL_FIELDNAME);

Check warning on line 490 in src/main/java/org/tailormap/api/solr/SolrHelper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/solr/SolrHelper.java#L490

Added line #L490 was not covered by tests
// see
// https://solr.apache.org/guide/solr/latest/query-guide/spatial-search.html#schema-configuration-for-rpt
FieldTypeDefinition spatialFieldTypeDef = new FieldTypeDefinition();
Expand All @@ -428,15 +506,17 @@ private void createSchemaIfNotExists() throws SolrServerException, IOException {
"maxDistErr", "0.001"));
spatialFieldAttributes.putAll(
Map.of(
"prefixTree", "packedQuad",
"prefixTree",
"packedQuad",
// see
// https://locationtech.github.io/spatial4j/apidocs/org/locationtech/spatial4j/context/jts/ValidationRule.html
"validationRule", "repairBuffer0",
"validationRule",
"repairBuffer0",
// NOTE THE ODDITY in coordinate order of "worldBounds",
// "ENVELOPE(minX, maxX, maxY, minY)"
"worldBounds",
// webmercator / EPSG:3857 projected bounds
"ENVELOPE(-20037508.34, 20037508.34, 20048966.1, -20048966.1)"
// webmercator / EPSG:3857 projected bounds
"ENVELOPE(-20037508.34, 20037508.34, 20048966.1, -20048966.1)"
// Amersfoort/RD new / EPSG:28992 projected bounds
// "ENVELOPE(482.06, 284182.97, 637049.52, 306602.42)"
));
Expand All @@ -445,47 +525,5 @@ private void createSchemaIfNotExists() throws SolrServerException, IOException {
new SchemaRequest.AddFieldType(spatialFieldTypeDef);
spatialFieldType.process(solrClient);
solrClient.commit();

logger.info("Creating Solr field {}", INDEX_GEOM_FIELD);
SchemaRequest.AddField schemaRequestGeom =
new SchemaRequest.AddField(
Map.of("name", INDEX_GEOM_FIELD, "type", "tm_geometry_rpt", "stored", true));
schemaRequestGeom.process(solrClient);

logger.info("Creating Solr field type {}", INDEX_DISPLAY_FIELD);
SchemaRequest.AddField displayFieldSchemaRequest =
new SchemaRequest.AddField(
Map.of(
"name", INDEX_DISPLAY_FIELD,
"type", "text_general",
"indexed", false,
"stored", true,
"multiValued", true,
"required", true,
"uninvertible", false));
displayFieldSchemaRequest.process(solrClient);

logger.info("Creating Solr field type {}", INDEX_SEARCH_FIELD);
SchemaRequest.AddField indexFieldSchemaRequest =
new SchemaRequest.AddField(
Map.of(
"name", INDEX_SEARCH_FIELD,
"type", "text_general",
"indexed", true,
"stored", true,
"multiValued", true,
"required", true,
"uninvertible", false));
indexFieldSchemaRequest.process(solrClient);
}

/**
* Close the wrapped Solr client.
*
* @throws IOException if an I/O error occurs
*/
@Override
public void close() throws IOException {
if (null != this.solrClient) this.solrClient.close();
}
}

0 comments on commit d710546

Please sign in to comment.