com.netflix.hollow.tools.query.HollowFieldMatchQuery Maven / Gradle / Ivy
/*
* Copyright 2016-2019 Netflix, Inc.
*
* Licensed 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 com.netflix.hollow.tools.query;
import com.netflix.hollow.core.read.HollowReadFieldUtils;
import com.netflix.hollow.core.read.engine.HollowReadStateEngine;
import com.netflix.hollow.core.read.engine.HollowTypeReadState;
import com.netflix.hollow.core.read.engine.object.HollowObjectTypeReadState;
import com.netflix.hollow.core.schema.HollowObjectSchema;
import com.netflix.hollow.core.schema.HollowObjectSchema.FieldType;
import com.netflix.hollow.core.schema.HollowSchema.SchemaType;
import com.netflix.hollow.tools.traverse.TransitiveSetTraverser;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;
/**
* A HollowFieldMatchQuery can be used to scan through all records in a dataset to match specific field name/value combinations.
*
* Results are returned in the form of a Map<String, BitSet>. Each type for which any records matched will have an entry in the
* returned Map, keyed by type name. The corresponding value is a BitSet which is set at the positions of the ordinals of
* the matched records.
*
* Hint: The returned Map<String, BitSet> may be in turn passed to the {@link TransitiveSetTraverser} to be augmented with any records
* which reference matched records. For example, we can imagine a data model for which the following code would provide a
* selection which includes the Actor record for "Tom Hanks", plus any Movie records in which he stars:
*
* {@code
* HollowFieldMatchQuery query = new HollowFieldMatchQuery(myStateEngine);
* Map<String, BitSet> selection = query.findMatchingRecords("actorName", "Tom Hanks");
*
* TransitiveSetTraverser.addReferencingOutsideClosure(myStateEngine, selection);
* }
*
*/
public class HollowFieldMatchQuery {
private final HollowReadStateEngine readEngine;
public HollowFieldMatchQuery(HollowReadStateEngine readEngine) {
this.readEngine = readEngine;
}
/**
* Match any records which include a field with the provided fieldName and value.
*
* @param fieldName the field name
* @param fieldValue the field value as a string that will be parsed as the type of the field to match.
* @return the matching records
*/
public Map findMatchingRecords(String fieldName, String fieldValue) {
Map matches = new HashMap();
for(HollowTypeReadState typeState : readEngine.getTypeStates()) {
augmentMatchingRecords(typeState, fieldName, fieldValue, matches);
}
return matches;
}
/**
* Match any records of the specified type, which have the specified field set to the specified value.
*
* @param typeName the type name
* @param fieldName the field name
* @param fieldValue the field value as a string that will be parsed as the type of the field to match.
* @return the matching records
*/
public Map findMatchingRecords(String typeName, String fieldName, String fieldValue) {
Map matches = new HashMap();
HollowTypeReadState typeState = readEngine.getTypeState(typeName);
if(typeState != null)
augmentMatchingRecords(typeState, fieldName, fieldValue, matches);
return matches;
}
private void augmentMatchingRecords(HollowTypeReadState typeState, String fieldName, String fieldValue, Map matches) {
if(typeState.getSchema().getSchemaType() == SchemaType.OBJECT) {
HollowObjectSchema schema = (HollowObjectSchema)typeState.getSchema();
for(int i=0;i 0)
matches.put(typeState.getSchema().getName(), typeQueryMatches);
}
}
}
}
private BitSet attemptReferenceTraversalQuery(HollowObjectTypeReadState typeState, int fieldIdx, String fieldValue) {
HollowTypeReadState referencedTypeState = typeState.getSchema().getReferencedTypeState(fieldIdx);
if(referencedTypeState.getSchema().getSchemaType() == SchemaType.OBJECT) {
HollowObjectTypeReadState refObjTypeState = (HollowObjectTypeReadState)referencedTypeState;
HollowObjectSchema refSchema = refObjTypeState.getSchema();
if(refSchema.numFields() == 1) {
if(refSchema.getFieldType(0) == FieldType.REFERENCE) {
BitSet refQueryMatches = attemptReferenceTraversalQuery(refObjTypeState, 0, fieldValue);
if(refQueryMatches != null)
return queryBasedOnMatchedReferences(typeState, fieldIdx, refQueryMatches);
} else {
Object queryValue = castQueryValue(fieldValue, refSchema.getFieldType(0));
if(queryValue != null) {
BitSet refQueryMatches = queryBasedOnValueMatches(refObjTypeState, 0, queryValue);
if(refQueryMatches.cardinality() > 0)
return queryBasedOnMatchedReferences(typeState, fieldIdx, refQueryMatches);
}
}
}
}
return null;
}
private BitSet queryBasedOnMatchedReferences(HollowObjectTypeReadState typeState, int referenceFieldPosition, BitSet matchedReferences) {
BitSet populatedOrdinals = typeState.getPopulatedOrdinals();
BitSet typeQueryMatches = new BitSet(populatedOrdinals.length());
int ordinal = populatedOrdinals.nextSetBit(0);
while(ordinal != -1) {
int refOrdinal = typeState.readOrdinal(ordinal, referenceFieldPosition);
if(refOrdinal != -1 && matchedReferences.get(refOrdinal))
typeQueryMatches.set(ordinal);
ordinal = populatedOrdinals.nextSetBit(ordinal+1);
}
return typeQueryMatches;
}
private BitSet queryBasedOnValueMatches(HollowObjectTypeReadState typeState, int fieldPosition, Object queryValue) {
BitSet populatedOrdinals = typeState.getPopulatedOrdinals();
BitSet typeQueryMatches = new BitSet(populatedOrdinals.length());
int ordinal = populatedOrdinals.nextSetBit(0);
while(ordinal != -1) {
if(HollowReadFieldUtils.fieldValueEquals(typeState, ordinal, fieldPosition, queryValue))
typeQueryMatches.set(ordinal);
ordinal = populatedOrdinals.nextSetBit(ordinal+1);
}
return typeQueryMatches;
}
private Object castQueryValue(String fieldValue, FieldType fieldType) {
try {
switch(fieldType) {
case BOOLEAN:
return Boolean.valueOf(fieldValue);
case DOUBLE:
return Double.parseDouble(fieldValue);
case FLOAT:
return Float.parseFloat(fieldValue);
case INT:
return Integer.parseInt(fieldValue);
case LONG:
return Long.parseLong(fieldValue);
case STRING:
return fieldValue;
default:
return null;
}
} catch(Exception e) {
return null;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy