Skip to content

Commit

Permalink
Merge pull request #696 from Netflix/feature/HollowHistoryCompositeKe…
Browse files Browse the repository at this point in the history
…ySearch

support Composite Key Search for HollowHistory tooling
  • Loading branch information
azeng-netflix authored Aug 28, 2024
2 parents 5ee4831 + 3ea9d81 commit a7140bd
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 36 deletions.
30 changes: 30 additions & 0 deletions hollow/src/main/java/com/netflix/hollow/core/util/IntList.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
package com.netflix.hollow.core.util;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

/**
* A list of primitive ints
Expand Down Expand Up @@ -106,4 +109,31 @@ public int hashCode() {
result = 31 * result + Arrays.hashCode(values);
return result;
}

public static Set<Integer> createSetFromIntList(IntList list) {
if (Objects.isNull(list)) {
return new HashSet<>();
}

HashSet<Integer> result = new HashSet<>(list.size());
int listSize = list.size();
for (int i = 0; i < listSize; ++i) {
result.add(list.get(i));
}

return result;
}

public static IntList createIntListFromSet(Set<Integer> set) {
if (Objects.isNull(set)) {
return new IntList(0);
}

IntList result = new IntList(set.size());
for (int value : set) {
result.add(value);
}

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.netflix.hollow.tools.history.keyindex;

import static com.netflix.hollow.core.HollowConstants.ORDINAL_NONE;
import static com.netflix.hollow.tools.util.SearchUtils.ESCAPED_MULTI_FIELD_KEY_DELIMITER;
import static com.netflix.hollow.tools.util.SearchUtils.MULTI_FIELD_KEY_DELIMITER;

import com.netflix.hollow.core.HollowDataset;
Expand All @@ -31,6 +32,8 @@
import com.netflix.hollow.core.util.RemovedOrdinalIterator;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Objects;
import java.util.Set;

public class HollowHistoryTypeKeyIndex {
private final PrimaryKey primaryKey;
Expand Down Expand Up @@ -175,50 +178,92 @@ public String getKeyDisplayString(int keyOrdinal) {
return builder.toString();
}

public IntList queryIndexedFields(final String query) {
IntList matchingKeys = new IntList();
private void getMatchesForField(int fieldIdx, String strVal, IntList matchingKeys) throws NumberFormatException {
int hashCode = 0;
Object objectToFind = null;
FieldType fieldType = fieldTypes[fieldIdx];
switch (fieldType) {
case INT:
final int queryInt = Integer.parseInt(strVal);
hashCode = HollowReadFieldUtils.intHashCode(queryInt);
objectToFind = queryInt;
break;
case LONG:
final long queryLong = Long.parseLong(strVal);
hashCode = HollowReadFieldUtils.longHashCode(queryLong);
objectToFind = queryLong;
break;
case STRING:
hashCode = HashCodes.hashCode(strVal);
objectToFind = strVal.replaceAll(ESCAPED_MULTI_FIELD_KEY_DELIMITER, MULTI_FIELD_KEY_DELIMITER);
break;
case DOUBLE:
final double queryDouble = Double.parseDouble(strVal);
hashCode = HollowReadFieldUtils.doubleHashCode(queryDouble);
objectToFind = queryDouble;
break;
case FLOAT:
final float queryFloat = Float.parseFloat(strVal);
hashCode = HollowReadFieldUtils.floatHashCode(queryFloat);
objectToFind = queryFloat;
break;
default:
}
ordinalMapping.addMatches(HashCodes.hashInt(hashCode), objectToFind, fieldIdx, fieldType, matchingKeys);
}

if (!isInitialized) {
return matchingKeys;
// find the exact matches for the given composite key.
private IntList queryIndexedFieldsForCompositeKey(final String[] compositeKeyComponents) {
IntList matchingKeys = new IntList();
Set<Integer> resultSet = null;
for (int i = 0; i < compositeKeyComponents.length; ++i) {
String currComponent = compositeKeyComponents[i];
try {
getMatchesForField(i, currComponent, matchingKeys);
Set<Integer> keySet = IntList.createSetFromIntList(matchingKeys);
matchingKeys.clear();
if (keySet.isEmpty()) {
// directly return as we'll not be able to find any exact matches for the given
// composite key.
return new IntList();
}
if (Objects.isNull(resultSet)) {
resultSet = keySet;
}
else {
resultSet.retainAll(keySet);
}
} catch (NumberFormatException ignore) {
return new IntList();
}
}

return IntList.createIntListFromSet(resultSet);
}

private IntList queryIndexedFieldsForNonCompositeKey(final String query) {
IntList matchingKeys = new IntList();
for (int i = 0; i < primaryKey.numFields(); i++) {
int hashCode = 0;
Object objectToFind = null;
try {
switch (fieldTypes[i]) {
case INT:
final int queryInt = Integer.parseInt(query);
hashCode = HollowReadFieldUtils.intHashCode(queryInt);
objectToFind = queryInt;
break;
case LONG:
final long queryLong = Long.parseLong(query);
hashCode = HollowReadFieldUtils.longHashCode(queryLong);
objectToFind = queryLong;
break;
case STRING:
hashCode = HashCodes.hashCode(query);
objectToFind = query;
break;
case DOUBLE:
final double queryDouble = Double.parseDouble(query);
hashCode = HollowReadFieldUtils.doubleHashCode(queryDouble);
objectToFind = queryDouble;
break;
case FLOAT:
final float queryFloat = Float.parseFloat(query);
hashCode = HollowReadFieldUtils.floatHashCode(queryFloat);
objectToFind = queryFloat;
break;
default:
}
ordinalMapping.addMatches(HashCodes.hashInt(hashCode), objectToFind, i, fieldTypes[i], matchingKeys);
getMatchesForField(i, query, matchingKeys);
} catch(NumberFormatException ignore) {}
}
return matchingKeys;
}

public IntList queryIndexedFields(final String query) {
if (!isInitialized) {
return new IntList();
}

String[] keyComponents = query.split("(?<!\\\\)" + MULTI_FIELD_KEY_DELIMITER, primaryKey.numFields());
if (keyComponents.length > 1 && keyComponents.length == primaryKey.numFields()) {
return queryIndexedFieldsForCompositeKey(keyComponents);
} else {
return queryIndexedFieldsForNonCompositeKey(query);
}
}

public Object getKeyFieldValue(int keyFieldIdx, int keyOrdinal) {
return ordinalMapping.getFieldObject(keyOrdinal, keyFieldIdx, fieldTypes[keyFieldIdx]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,15 @@ public void extractsAndIndexesKeyRecords() throws IOException {

Assert.assertEquals("2.2:two", keyIdx.getKeyDisplayString("A", 1));


/// query returns all matching keys
assertResults(keyIdx, "A", "1.1:one", 0);
assertResults(keyIdx, "A", "3.3:one", 2);
assertResults(keyIdx, "A", "4.4:four", 3);
assertResults(keyIdx, "A", "5.5:five!", 4);
assertResults(keyIdx, "A", "5.5!:five!" );
assertResults(keyIdx, "A", "5.5:five!:" );
assertResults(keyIdx, "A", "5.5:");

assertResults(keyIdx, "A", "one", 0, 2);
assertResults(keyIdx, "A", "two", 1);
assertResults(keyIdx, "A", "four", 3);
Expand All @@ -132,7 +139,6 @@ private void assertResults(HollowHistoryKeyIndex keyIdx, String type, String que
Assert.assertEquals(expectedResults.length, actualResults.size());

actualResults.sort();

for(int i=0;i<expectedResults.length;i++) {
Assert.assertEquals(expectedResults[i], actualResults.get(i));
}
Expand Down

0 comments on commit a7140bd

Please sign in to comment.