Permalink
Browse files

Reduced number of search navigators refresh requests in JS resort mode

The SearchEvent listen to changes on each of its navigators, and the
information about their overall state is sent with each fetched search
item (as a "data-nav-generation" attribute). Then the browser can
regularly fetch a fresh version of yacysearchtrailer.html only if
necessary (when that nav-generation value change).
  • Loading branch information...
luccioman committed Oct 12, 2017
1 parent 2ac78e2 commit 8303e15419e789cad94b94a1d65e00f9627cd5f1
@@ -26,8 +26,11 @@ var currentPageNumber = 0;
/* Set to true to enable browser console log traces */
var logEnabled = false;
/* Indicates if the results feeders are running on the server */
var feedRunning = true;
/* Indicates if the results fetching from server is running */
var fetchingResults = true;
/* Holds the last known navigators generation known from the server */
var lastNavGeneration = null;
/**
* Refresh the results page, checking and eventually updating each result CSS class depending on its position.
@@ -255,35 +258,67 @@ var processSidebar = function(data) {
// TODO: nav-vocabulary
// TODO: nav-about
/* Store the new nav-generation data attribute if provided */
var navGenerationHolder = $("#rankingButtons");
if(navGenerationHolder.length > 0) {
var newNavGenerationHolder = newSidebar.find("#rankingButtons");
if(newNavGenerationHolder.length > 0) {
var navGeneration = newNavGenerationHolder.data("nav-generation");
if(navGeneration != null) {
navGenerationHolder.data("nav-generation", navGeneration);
lastNavGeneration = navGeneration;
}
}
}
}
// TODO: figure out if a better timeout strategy is feasible
if(feedRunning) {
if(fetchingResults) {
setTimeout(updateSidebar, 500);
}
};
/**
* Update the search navigators (facets) sidebar if necessary.
*/
var updateSidebar = function() {
var trailerParams = {
var navGenerationHolder = $("#rankingButtons");
var shouldUpdateSideBar = true;
if(navGenerationHolder.length > 0) {
var oldNavGeneration = navGenerationHolder.data("nav-generation");
if(oldNavGeneration != null && lastNavGeneration != null && oldNavGeneration == lastNavGeneration) {
/* nav-generation has not changed : no need to refresh the navigators side bar*/
shouldUpdateSideBar = false;
if(logEnabled) {
console.log("Prevented unnecessary sidebar update");
}
}
}
if(shouldUpdateSideBar) {
var trailerParams = {
"eventID": theEventID,
"resource": "global"
};
var searchForm = document.forms.searchform;
if(searchForm != null) {
if(searchForm.resource != null && searchForm.resource.value != null) {
trailerParams.resource = searchForm.resource.value;
}
if(searchForm.auth != null && searchForm.auth.value != null) {
trailerParams.auth = searchForm.auth.value;
}
}
$.get(
"yacysearchtrailer.html",
trailerParams,
processSidebar
);
//$("#sidebar").load("yacysearchtrailer.html", {"eventID": theEventID});
};
var searchForm = document.forms.searchform;
if(searchForm != null) {
if(searchForm.resource != null && searchForm.resource.value != null) {
trailerParams.resource = searchForm.resource.value;
}
if(searchForm.auth != null && searchForm.auth.value != null) {
trailerParams.auth = searchForm.auth.value;
}
}
$.get(
"yacysearchtrailer.html",
trailerParams,
processSidebar
);
} else if(fetchingResults) {
setTimeout(updateSidebar, 500);
}
};
/**
@@ -296,7 +331,7 @@ var processLatestInfo = function(latestInfo) {
eventID : theEventID
}, processItem);
} else {
feedRunning = false;
fetchingResults = false;
}
}
@@ -355,6 +390,12 @@ var processItem = function(data) {
}
});
}
var navGeneration = newItem.data("nav-generation");
if(navGeneration != null) {
/* Store the navigators generation new value when the item provided this info */
lastNavGeneration = navGeneration;
}
displayPage(false, currentPageNumber);
@@ -1,5 +1,5 @@
#(content)#::
<div class="searchresults" data-ranking="#[ranking]#">
<div class="searchresults" data-ranking="#[ranking]#" data-nav-generation="#[nav-generation]#">
<h4 class="linktitle">
#(favicon)#::
<img width="16" height="16" src="#[faviconUrl]#" id="f#[urlhash]#" class="favicon" style="width:16px; height:16px;" alt="" />
@@ -177,6 +177,10 @@ public static serverObjects respond(final RequestHeader header, final serverObje
prop.putXML("content_title-xml", result.title());
prop.putJSON("content_title-json", result.title());
prop.putHTML("content_showPictures_link", resultUrlstring);
/* Add information about the current search navigators to let browser refresh yacysearchtrailer only if needed */
prop.put("content_nav-generation", theSearch.getNavGeneration());
//prop.putHTML("content_link", resultUrlstring);
// START interaction
@@ -391,6 +395,7 @@ public static serverObjects respond(final RequestHeader header, final serverObje
prop.put("content_loc_lat", result.lat());
prop.put("content_loc_lon", result.lon());
}
final boolean clustersearch = sb.isRobinsonMode() && sb.getConfig(SwitchboardConstants.CLUSTER_MODE, "").equals(SwitchboardConstants.CLUSTER_MODE_PUBLIC_CLUSTER);
final boolean indexReceiveGranted = sb.getConfigBool(SwitchboardConstants.INDEX_RECEIVE_ALLOW_SEARCH, true) || clustersearch;
boolean p2pmode = sb.peers != null && sb.peers.sizeConnected() > 0 && indexReceiveGranted;
@@ -43,7 +43,8 @@
#(/resource-select)#
<p class="navbutton"></p>
<div class="btn-group btn-group-justified">
<div id="rankingButtons" class="btn-group btn-group-justified" data-nav-generation="#[nav-generation]#">
<!-- data-nav-generation attribute helps the browser know whether the search navigators have to be refreshed -->
<div class="btn-group btn-group-xs"><button type="button" id="sort_button_context" class="btn btn-default" onclick="document.getElementById('search').value=document.getElementById('search').value.replace(' /date','');document.searchform.submit();">Context Ranking</button></div>
<div class="btn-group btn-group-xs"><button type="button" id="sort_button_date" class="btn btn-default" onclick="document.getElementById('search').value=document.getElementById('search').value + ' /date';document.searchform.submit();">Sort by Date</button></div>
</div>
@@ -100,6 +100,9 @@ public static serverObjects respond(final RequestHeader header, final serverObje
return prop;
}
final RequestHeader.FileType fileType = header.fileType();
/* Add information about the current navigators generation (number of updates since their initialization) */
prop.put("nav-generation", theSearch.getNavGeneration());
// compose search navigation
ContentDomain contentdom = theSearch.getQuery().contentdom;
@@ -72,32 +72,30 @@ public synchronized void clear() {
this.encnt = 0;
}
/**
* shrink the cluster to a demanded size
* @param maxsize
*/
@Override
public void shrinkToMaxSize(final int maxsize) {
if (maxsize < 0) return;
public int shrinkToMaxSize(final int maxsize) {
if (maxsize < 0) {
return 0;
}
Long key;
int deletedNb = 0;
synchronized (this) {
while (this.map.size() > maxsize) {
// find and remove smallest objects until cluster has demanded size
key = this.pam.firstKey();
if (key == null) break;
this.map.remove(this.pam.remove(key));
deletedNb++;
}
}
return deletedNb;
}
/**
* shrink the cluster in such a way that the smallest score is equal or greater than a given minScore
* @param minScore
*/
@Override
public void shrinkToMinScore(final int minScore) {
public int shrinkToMinScore(final int minScore) {
int score;
Long key;
int deletedNb = 0;
synchronized (this) {
while (!this.pam.isEmpty()) {
// find and remove objects where their score is smaller than the demanded minimum score
@@ -106,8 +104,10 @@ public void shrinkToMinScore(final int minScore) {
score = (int) ((key.longValue() & 0xFFFFFFFF00000000L) >> 32);
if (score >= minScore) break;
this.map.remove(this.pam.remove(key));
deletedNb++;
}
}
return deletedNb;
}
private long scoreKey(final int elementNr, final int elementCount) {
@@ -40,12 +40,35 @@
public class ConcurrentScoreMap<E> extends AbstractScoreMap<E> implements ScoreMap<E> {
protected final ConcurrentHashMap<E, AtomicInteger> map; // a mapping from a reference to the cluster key
private long gcount; // sum of all scores
/** a mapping from a reference to the cluster key */
protected final ConcurrentHashMap<E, AtomicInteger> map;
/** sum of all scores */
private long gcount;
/** Eventual registered object listening on map updates */
private ScoreMapUpdatesListener updatesListener;
public ConcurrentScoreMap() {
this(null);
}
/**
* @param updatesListener an eventual object listening on score map updates
*/
public ConcurrentScoreMap(final ScoreMapUpdatesListener updatesListener) {
this.map = new ConcurrentHashMap<E, AtomicInteger>();
this.gcount = 0;
this.updatesListener = updatesListener;
}
/**
* Dispatch the update event to the eventually registered listener.
*/
private void dispatchUpdateToListener() {
if(this.updatesListener != null) {
this.updatesListener.updatedScoreMap();
}
}
@Override
@@ -57,34 +80,40 @@ public ConcurrentScoreMap() {
public synchronized void clear() {
this.map.clear();
this.gcount = 0;
dispatchUpdateToListener();
}
/**
* shrink the cluster to a demanded size
* @param maxsize
*/
@Override
public void shrinkToMaxSize(final int maxsize) {
if (this.map.size() <= maxsize) return;
public int shrinkToMaxSize(final int maxsize) {
if (this.map.size() <= maxsize) {
return 0;
}
int deletedNb = 0;
int minScore = getMinScore();
while (this.map.size() > maxsize) {
minScore++;
shrinkToMinScore(minScore);
deletedNb += shrinkToMinScore(minScore);
}
// No need to dispatch to listener, it is already done in shrinkToMinScore()
return deletedNb;
}
/**
* shrink the cluster in such a way that the smallest score is equal or greater than a given minScore
* @param minScore
*/
@Override
public void shrinkToMinScore(final int minScore) {
public int shrinkToMinScore(final int minScore) {
final Iterator<Map.Entry<E, AtomicInteger>> i = this.map.entrySet().iterator();
Map.Entry<E, AtomicInteger> entry;
int deletedNb = 0;
while (i.hasNext()) {
entry = i.next();
if (entry.getValue().intValue() < minScore) i.remove();
if (entry.getValue().intValue() < minScore) {
i.remove();
deletedNb++;
}
}
if(deletedNb > 0) {
dispatchUpdateToListener();
}
return deletedNb;
}
public long totalCount() {
@@ -116,6 +145,8 @@ public void inc(final E obj) {
// increase overall counter
this.gcount++;
dispatchUpdateToListener();
}
@Override
@@ -128,6 +159,8 @@ public void dec(final E obj) {
// increase overall counter
this.gcount--;
dispatchUpdateToListener();
}
@Override
@@ -143,6 +176,8 @@ public void set(final E obj, final int newScore) {
}
// increase overall counter
this.gcount += newScore;
dispatchUpdateToListener();
}
@Override
@@ -155,6 +190,8 @@ public void inc(final E obj, final int incrementScore) {
// increase overall counter
this.gcount += incrementScore;
dispatchUpdateToListener();
}
@Override
@@ -171,6 +208,9 @@ public int delete(final E obj) {
// decrease overall counter
this.gcount -= score.intValue();
dispatchUpdateToListener();
return score.intValue();
}
@@ -266,6 +306,13 @@ public String toString() {
}
return sortedKeys.iterator();
}
/**
* @param updatesListener an eventual object which wants to listen to successful updates on this score map
*/
public void setUpdatesListener(final ScoreMapUpdatesListener updatesListener) {
this.updatesListener = updatesListener;
}
public static void main(final String[] args) {
final ConcurrentScoreMap<String> a = new ConcurrentScoreMap<String>();
Oops, something went wrong.

0 comments on commit 8303e15

Please sign in to comment.