blob: bc26ae91b043dcd7cf41cbcdf23d2cef8a90a24a [file] [log] [blame]
/*
* 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.usergrid.persistence.index.impl;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.index.IndexEdge;
import org.apache.usergrid.persistence.model.entity.Entity;
import org.apache.usergrid.persistence.model.entity.EntityMap;
import org.apache.usergrid.persistence.model.entity.Id;
import com.google.common.base.Optional;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.APPLICATION_ID_FIELDNAME;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.EDGE_NAME_FIELDNAME;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.EDGE_NODE_ID_FIELDNAME;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.EDGE_NODE_TYPE_FIELDNAME;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.EDGE_SEARCH_FIELDNAME;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.EDGE_TIMESTAMP_FIELDNAME;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.ENTITY_FIELDS;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.ENTITY_ID_FIELDNAME;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.ENTITY_SIZE_FIELDNAME;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.ENTITY_TYPE_FIELDNAME;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.ENTITY_VERSION_FIELDNAME;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.applicationId;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.entityId;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.getType;
import static org.apache.usergrid.persistence.index.impl.IndexingUtils.nodeId;
/**
* Convert a CP entity to an elasticsearch document
*/
public class EntityToMapConverter {
public static Map<String, Object> convert(ApplicationScope applicationScope, final IndexEdge indexEdge,
final Entity entity) {
return convert( applicationScope, indexEdge, entity, Optional.absent() );
}
/**
* Set the entity as a map with the context
*
* @param applicationScope
* @param entity The entity
* @param indexEdge The edge this entity is indexed on
* @param fieldsToIndex A set of fields that will be indexed should they exist on the entity. Other fields will be filtered out.
*/
public static Map<String, Object> convert(ApplicationScope applicationScope, final IndexEdge indexEdge,
final Entity entity, Optional<Set<String>> fieldsToIndex) {
final Map<String, Object> outputEntity = new HashMap<>();
final Id entityId = entity.getId();
/***
* Add our static fields for easier admin/debugging/reporting
****/
outputEntity.put( ENTITY_ID_FIELDNAME, entityId( entityId ) );
outputEntity.put( ENTITY_VERSION_FIELDNAME, entity.getVersion() );
outputEntity.put( ENTITY_TYPE_FIELDNAME, getType( applicationScope, entityId));
outputEntity.put( APPLICATION_ID_FIELDNAME, applicationId( applicationScope.getApplication() ) );
outputEntity.put( EDGE_NODE_ID_FIELDNAME, nodeId( indexEdge.getNodeId() ) );
outputEntity.put( EDGE_NODE_TYPE_FIELDNAME, indexEdge.getNodeType() );
outputEntity.put( EDGE_NAME_FIELDNAME, indexEdge.getEdgeName() );
outputEntity.put( EDGE_TIMESTAMP_FIELDNAME, indexEdge.getTimestamp() );
outputEntity.put( ENTITY_SIZE_FIELDNAME, entity.getSize() );
//add the context for filtering later
outputEntity.put( EDGE_SEARCH_FIELDNAME, IndexingUtils.createContextName( applicationScope, indexEdge ) );
//migrate the entity to map since we're ultimately going to use maps once we get rid of the Field objects
final EntityMap entityMap = EntityMap.fromEntity( entity );
//now visit our entity
final FieldParser parser = new EntityMappingParser();
final Set<EntityField> fieldsToBeFiltered = parser.parse( entityMap );
//add our fields to output entity
outputEntity.put( ENTITY_FIELDS, fieldsToBeFiltered );
if(fieldsToIndex.isPresent()){
Set<String> defaultProperties = fieldsToIndex.get();
HashSet mapFields = ( HashSet ) outputEntity.get( "fields" );
Iterator collectionIterator = mapFields.iterator();
//Loop through all of the fields of the flatted entity and check to see if they should be filtered out.
collectionIterator.forEachRemaining(outputEntityField -> {
EntityField testedField = ( EntityField ) outputEntityField;
String fieldName = ( String ) ( testedField ).get( "name" );
//could move this down into the method below
if ( !defaultProperties.contains( fieldName ) ) {
iterateThroughMapForFieldsToBeIndexed( defaultProperties, collectionIterator, fieldName );
}
});
}
return outputEntity;
}
/**
* Handles checking to see if a field is a top level exclusion or just a field that shouldn't be indexed.
* This is handled by looping through all the fields we want to be able to query on, and checking to see if a
* specific field name is included. If the field name is included then do nothing. If the field name is not included
* then we do not want to be able to query on it. Instead we remove it from the collectionIterator which
* removes it from the outputEntity above, thus filtering it out.
*
* @param fieldsToKeep - contains a list of fields that the user defined in their schema.
* @param collectionIterator - contains the iterator with the reference to the map where we want to remove the field. Once removed here it is removed from the entity so it won't be indexed.
* @param fieldName - contains the name of the field that we want to keep.
*/
private static void iterateThroughMapForFieldsToBeIndexed( final Set<String> fieldsToKeep,
final Iterator collectionIterator,
final String fieldName ) {
boolean toRemoveFlag = true;
Iterator fieldIterator = fieldsToKeep.iterator();
//goes through a loop of all the fields that we want to keep.
//if the toRemoveFlag is set to false then we want to keep the property and do nothing to it, otherwise we set it to true and remove
//the property.
while ( fieldIterator.hasNext() ) {
//this is the field that we're
String fieldToKeep = ( String ) fieldIterator.next();
//Since we know that the fieldName cannot be equal to the requiredInclusion criteria due to the if condition before we enter this method
//and we are certain that the indexing criteria is shorter we want to be sure that the inclusion criteria
//is contained within the field we're evaluating. i.e that one.two.three contains one.two
//The second part of the if loop also requires that the fieldName is followed by a period after we check to ensure that the
//indexing criteria is included in the string. This is done to weed out values such as one.twoexample.three
// when we should only keep one.two.three when comparing the indexing criteria of one.two.
if(fieldName.length() > fieldToKeep.length()
&& fieldName.contains( fieldToKeep )
&& fieldName.charAt( fieldToKeep.length() )=='.' ) {
toRemoveFlag = false;
break;
}
else {
//the of the field we're evaluating is shorter than the indexing criteria so it can't match.
//Move onto the next field and see if they match.
toRemoveFlag = true;
}
}
if ( toRemoveFlag ) {
collectionIterator.remove();
}
}
}