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

org.apache.solr.handler.designer.ManagedSchemaDiff 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.handler.designer;

import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.common.util.SuppressForbidden;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.ManagedIndexSchema;
import org.apache.solr.schema.SchemaField;

/** Utility methods for comparing managed index schemas */
public class ManagedSchemaDiff {

  private static final String UPDATED_KEY_STRING = "updated";
  private static final String ADDED_KEY_STRING = "added";
  private static final String REMOVED_KEY_STRING = "removed";

  private static final String FIELDS_KEY_STRING = "fields";
  private static final String FIELD_TYPES_KEY_STRING = "fieldTypes";
  private static final String DYNAMIC_FIELDS_KEY_STRING = "dynamicFields";
  private static final String COPY_FIELDS_KEY_STRING = "copyFields";

  /**
   * Compute difference between two managed schemas. The returned map consists of changed, new,
   * removed elements in fields, field types, dynamic fields and copy fields between input schemas.
   *
   * 
 Output format when rendered to json will look like below:
   *   {@code
   *   {
   *     "fields": {
   *       "updated": {...},
   *       "added": {...},
   *       "removed": {...}
   *     },
   *     "fieldTypes": {
   *       "updated": {...},
   *       "added": {...},
   *       "removed": {...}
   *     },
   *     "dynamicFields": {
   *       "updated": {...},
   *       "added": {...},
   *       "removed": {...}
   *     },
   *     "copyFields: {
   *       "new": [...],
   *       "old": [...]
   *     }
   *   }
   *   }
   * 
* * @param oldSchema instance of {@link ManagedIndexSchema} * @param newSchema instance of {@link ManagedIndexSchema} * @return the difference between two schemas */ public static Map diff( ManagedIndexSchema oldSchema, ManagedIndexSchema newSchema) { Map diff = new HashMap<>(); Map fieldsDiff = diff( mapFieldsToPropertyValues(oldSchema.getFields()), mapFieldsToPropertyValues(newSchema.getFields())); Map fieldTypesDiff = diff( mapFieldTypesToPropValues(oldSchema.getFieldTypes()), mapFieldTypesToPropValues(newSchema.getFieldTypes())); Map dynamicFieldDiff = diff( mapDynamicFieldToPropValues(oldSchema.getDynamicFields()), mapDynamicFieldToPropValues(newSchema.getDynamicFields())); Map copyFieldDiff = diff(getCopyFieldList(oldSchema), getCopyFieldList(newSchema)); if (!fieldsDiff.isEmpty()) { diff.put(FIELDS_KEY_STRING, fieldsDiff); } if (!fieldTypesDiff.isEmpty()) { diff.put(FIELD_TYPES_KEY_STRING, fieldTypesDiff); } if (!dynamicFieldDiff.isEmpty()) { diff.put(DYNAMIC_FIELDS_KEY_STRING, dynamicFieldDiff); } if (!copyFieldDiff.isEmpty()) { diff.put(COPY_FIELDS_KEY_STRING, copyFieldDiff); } return diff; } /** * Compute difference between two map objects with {@link SimpleOrderedMap} as values. * *
 Example of the output format when rendered to json
   *   {@code
   *    {
   *      "updated": {
   *        "stringField": [
   *        {
   *          "docValues": "false"
   *        },
   *        {
   *          "docValues": "true"
   *        }
   *      },
   *      "added": {
   *        "newstringfield: {
   *          "name": "newstringfield",
   *          "type": "string",
   *          .....
   *        }
   *      },
   *      "removed": {
   *        "oldstringfield": {
   *          "name": "oldstringfield",
   *          "type": "string",
   *          .....
   *        }
   *      }
   *    }
   *   }
   * 
* * @param map1 instance of Map with {@link SimpleOrderedMap} elements * @param map2 instance of Map with {@link SimpleOrderedMap} elements * @return the difference between two Map */ protected static Map diff( Map> map1, Map> map2) { Map>> changedValues = new HashMap<>(); Map> newValues = new HashMap<>(); Map> removedValues = new HashMap<>(); for (String fieldName : map1.keySet()) { if (map2.containsKey(fieldName)) { SimpleOrderedMap oldPropValues = map1.get(fieldName); SimpleOrderedMap newPropValues = map2.get(fieldName); if (!oldPropValues.equals(newPropValues)) { List> mapDiff = getMapDifference(oldPropValues, newPropValues); if (!mapDiff.isEmpty()) { changedValues.put(fieldName, mapDiff); } } } else { removedValues.put(fieldName, map1.get(fieldName)); } } for (String fieldName : map2.keySet()) { if (!map1.containsKey(fieldName)) { newValues.put(fieldName, map2.get(fieldName)); } } Map mapDiff = new HashMap<>(); if (!changedValues.isEmpty()) { mapDiff.put(UPDATED_KEY_STRING, changedValues); } if (!newValues.isEmpty()) { mapDiff.put(ADDED_KEY_STRING, newValues); } if (!removedValues.isEmpty()) { mapDiff.put(REMOVED_KEY_STRING, removedValues); } return mapDiff; } /** * Compute difference between two {@link SimpleOrderedMap} instances * *
 Output format example when rendered to json
   *   {@code
   *    [
   *         {
   *           "stored": false,
   *           "type": "string",
   *           "multiValued": false
   *         },
   *         {
   *           "stored": true,
   *           "type": "strings",
   *           "multiValued": true
   *         }
   *       ]
   *   }
   * 
* * @param simpleOrderedMap1 Map to treat as "left" map * @param simpleOrderedMap2 Map to treat as "right" map * @return List containing the left diff and right diff */ @SuppressForbidden(reason = "Maps.difference") private static List> getMapDifference( SimpleOrderedMap simpleOrderedMap1, SimpleOrderedMap simpleOrderedMap2) { Map map1 = simpleOrderedMap1.toMap(new HashMap<>()); Map map2 = simpleOrderedMap2.toMap(new HashMap<>()); Map> mapDiff = Maps.difference(map1, map2).entriesDiffering(); if (mapDiff.isEmpty()) { return Collections.emptyList(); } Map leftMapDiff = mapDiff.entrySet().stream() .map(entry -> Map.entry(entry.getKey(), entry.getValue().leftValue())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); Map rightMapDiff = mapDiff.entrySet().stream() .map(entry -> Map.entry(entry.getKey(), entry.getValue().rightValue())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); return Arrays.asList(leftMapDiff, rightMapDiff); } protected static Map diff( List> list1, List> list2) { List> oldList = new ArrayList<>(); // ordered map changed in list1 compared to list2 List> newList = new ArrayList<>(); // ordered map changed in list2 compared to list1 list1.forEach( som -> { if (!list2.contains(som)) { oldList.add(som); } }); list2.forEach( som -> { if (!list1.contains(som)) { newList.add(som); } }); Map mapDiff = new HashMap<>(); if (!oldList.isEmpty()) { mapDiff.put("old", oldList); } if (!newList.isEmpty()) { mapDiff.put("new", newList); } return mapDiff; } protected static Map> mapFieldsToPropertyValues( Map fields) { Map> propValueMap = new HashMap<>(); fields.forEach((k, v) -> propValueMap.put(k, v.getNamedPropertyValues(true))); return propValueMap; } protected static Map> mapFieldTypesToPropValues( Map fieldTypes) { Map> propValueMap = new HashMap<>(); fieldTypes.forEach((k, v) -> propValueMap.put(k, v.getNamedPropertyValues(true))); return propValueMap; } protected static Map> mapDynamicFieldToPropValues( IndexSchema.DynamicField[] dynamicFields) { return Stream.of(dynamicFields) .map( df -> Map.entry( df.getPrototype().getName(), df.getPrototype().getNamedPropertyValues(true))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } protected static List> getCopyFieldList(ManagedIndexSchema indexSchema) { return indexSchema.getCopyFieldProperties(false, null, null); } }