-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: index mechanism to enhance overall performance
- Loading branch information
Showing
33 changed files
with
1,746 additions
and
1,132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
api/src/main/java/run/halo/app/extension/index/IndexEntryOperator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package run.halo.app.extension.index; | ||
|
||
import java.util.Collection; | ||
import java.util.NavigableSet; | ||
import java.util.Set; | ||
|
||
public interface IndexEntryOperator { | ||
|
||
/** | ||
* Search all values that key less than the target key. | ||
* | ||
* @param key target key | ||
* @param orEqual whether to include the value of the target key | ||
* @return object names that key less than the target key | ||
*/ | ||
NavigableSet<String> lessThan(String key, boolean orEqual); | ||
|
||
/** | ||
* Search all values that key greater than the target key. | ||
* | ||
* @param key target key | ||
* @param orEqual whether to include the value of the target key | ||
* @return object names that key greater than the target key | ||
*/ | ||
NavigableSet<String> greaterThan(String key, boolean orEqual); | ||
|
||
/** | ||
* Search all values that key in the range of [start, end]. | ||
* | ||
* @param start start key | ||
* @param end end key | ||
* @param startInclusive whether to include the value of the start key | ||
* @param endInclusive whether to include the value of the end key | ||
* @return object names that key in the range of [start, end] | ||
*/ | ||
NavigableSet<String> range(String start, String end, boolean startInclusive, | ||
boolean endInclusive); | ||
|
||
/** | ||
* Find all values that key equals to the target key. | ||
* | ||
* @param key target key | ||
* @return object names that key equals to the target key | ||
*/ | ||
NavigableSet<String> find(String key); | ||
|
||
NavigableSet<String> findIn(Collection<String> keys); | ||
|
||
/** | ||
* Get all values in the index entry. | ||
* | ||
* @return a set of all object names | ||
*/ | ||
Set<String> getValues(); | ||
} |
178 changes: 178 additions & 0 deletions
178
api/src/main/java/run/halo/app/extension/index/IndexEntryOperatorImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
package run.halo.app.extension.index; | ||
|
||
import java.util.AbstractMap; | ||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.HashSet; | ||
import java.util.LinkedHashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.NavigableSet; | ||
import java.util.Set; | ||
import java.util.TreeSet; | ||
import org.springframework.util.Assert; | ||
|
||
public class IndexEntryOperatorImpl implements IndexEntryOperator { | ||
private final IndexEntry indexEntry; | ||
|
||
public IndexEntryOperatorImpl(IndexEntry indexEntry) { | ||
this.indexEntry = indexEntry; | ||
} | ||
|
||
private static NavigableSet<String> createNavigableSet() { | ||
return new TreeSet<>(KeyComparator.INSTANCE); | ||
} | ||
|
||
@Override | ||
public NavigableSet<String> lessThan(String key, boolean orEqual) { | ||
Assert.notNull(key, "Key must not be null."); | ||
indexEntry.acquireReadLock(); | ||
try { | ||
var sortedEntries = sortedEntries(); | ||
var index = binarySearch(sortedEntries, key); | ||
|
||
var resultSet = createNavigableSet(); | ||
|
||
if (index < 0) { | ||
// if key not found, get the insertion point(-index-1) | ||
index = -index - 1; | ||
} else if (orEqual) { | ||
// key exists and includes equal, index+1 | ||
index++; | ||
} | ||
|
||
// Add all values if key less than the target key (or including the target key) to the | ||
// result set | ||
for (int i = 0; i < index; i++) { | ||
resultSet.add(sortedEntries.get(i).getValue()); | ||
} | ||
return resultSet; | ||
} finally { | ||
indexEntry.releaseReadLock(); | ||
} | ||
} | ||
|
||
@Override | ||
public NavigableSet<String> greaterThan(String key, boolean orEqual) { | ||
Assert.notNull(key, "Key must not be null."); | ||
indexEntry.acquireReadLock(); | ||
try { | ||
var sortedEntries = sortedEntries(); | ||
var index = binarySearch(sortedEntries, key); | ||
|
||
var resultSet = createNavigableSet(); | ||
|
||
if (index < 0) { | ||
// key does not exist, get the insertion point (-index-1) | ||
index = -index - 1; | ||
} else if (!orEqual) { | ||
// key exists but does not include equal, index+1 | ||
index++; | ||
} | ||
|
||
for (int i = index; i < sortedEntries.size(); i++) { | ||
resultSet.add(sortedEntries.get(i).getValue()); | ||
} | ||
|
||
return resultSet; | ||
} finally { | ||
indexEntry.releaseReadLock(); | ||
} | ||
} | ||
|
||
@Override | ||
public NavigableSet<String> range(String start, String end, boolean startInclusive, | ||
boolean endInclusive) { | ||
Assert.notNull(start, "The start must not be null."); | ||
Assert.notNull(end, "The end must not be null."); | ||
indexEntry.acquireReadLock(); | ||
try { | ||
var sortedEntries = sortedEntries(); | ||
|
||
var resultSet = createNavigableSet(); | ||
|
||
for (Map.Entry<String, String> entry : sortedEntries) { | ||
String key = entry.getKey(); | ||
boolean greaterThanStart = | ||
startInclusive ? key.compareTo(start) >= 0 : key.compareTo(start) > 0; | ||
boolean lessThanEnd = | ||
endInclusive ? key.compareTo(end) <= 0 : key.compareTo(end) < 0; | ||
|
||
if (greaterThanStart && lessThanEnd) { | ||
resultSet.add(entry.getValue()); | ||
} | ||
} | ||
return resultSet; | ||
} finally { | ||
indexEntry.releaseReadLock(); | ||
} | ||
} | ||
|
||
@Override | ||
public NavigableSet<String> find(String key) { | ||
Assert.notNull(key, "The key must not be null."); | ||
indexEntry.acquireReadLock(); | ||
try { | ||
var resultSet = createNavigableSet(); | ||
|
||
for (Map.Entry<String, String> entry : sortedEntries()) { | ||
if (entry.getKey().equals(key)) { | ||
resultSet.add(entry.getValue()); | ||
} | ||
} | ||
|
||
return resultSet; | ||
} finally { | ||
indexEntry.releaseReadLock(); | ||
} | ||
} | ||
|
||
@Override | ||
public NavigableSet<String> findIn(Collection<String> keys) { | ||
if (keys == null || keys.isEmpty()) { | ||
return createNavigableSet(); | ||
} | ||
var keysToSearch = new HashSet<>(keys); | ||
indexEntry.acquireReadLock(); | ||
try { | ||
var resultSet = createNavigableSet(); | ||
|
||
for (Map.Entry<String, String> entry : sortedEntries()) { | ||
if (keysToSearch.contains(entry.getKey())) { | ||
resultSet.add(entry.getValue()); | ||
} | ||
} | ||
|
||
return resultSet; | ||
} finally { | ||
indexEntry.releaseReadLock(); | ||
} | ||
} | ||
|
||
@Override | ||
public Set<String> getValues() { | ||
indexEntry.acquireReadLock(); | ||
try { | ||
Set<String> uniqueValues = new LinkedHashSet<>(); | ||
for (Map.Entry<String, String> entry : sortedEntries()) { | ||
uniqueValues.add(entry.getValue()); | ||
} | ||
return uniqueValues; | ||
} finally { | ||
indexEntry.releaseReadLock(); | ||
} | ||
} | ||
|
||
int binarySearch(List<Map.Entry<String, String>> sortedEntries, String key) { | ||
var searchEntry = new AbstractMap.SimpleEntry<String, String>(key, null); | ||
return Collections.binarySearch(sortedEntries, searchEntry, | ||
Map.Entry.comparingByKey() | ||
); | ||
} | ||
|
||
private ArrayList<Map.Entry<String, String>> sortedEntries() { | ||
//index entries are sorted by key, so we can use the entries directly | ||
return new ArrayList<>(indexEntry.entries()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.