/*
* Copyright (C) 2004 Paul Browne, http://www.firstpartners.net,
* built with the help of Fast-Soft (fastsoftdev@yahoo.com)
*
* released under terms of the GPL license
* http://www.opensource.org/licenses/gpl-license.php
*
* This product includes software developed by the
* Apache Software Foundation (http://www.apache.org)."
*
* This product includes software developed by the
* Spring Framework Project (http://www.springframework.org)."
*
*/
package net.fp.rp.search.mid.intelligence;
import net.fp.rp.common.exception.RpException;
import net.fp.rp.search.back.datastore.BasicFeedbackDataStore;
import net.fp.rp.search.back.search.SearchQuery;
import net.fp.rp.search.back.struct.DocumSetStruct;
import net.fp.rp.search.back.struct.DocumStruct;
import net.fp.rp.search.common.util.Util;
import net.fp.rp.search.mid.feedback.BaseFeedback;
import net.fp.rp.search.mid.feedback.CategoryFeedback;
import net.fp.rp.search.mid.feedback.DocumentFeedback;
import net.fp.rp.search.mid.feedback.SearchFeedback;
import net.fp.rp.search.mid.global.PluginManager;
import net.fp.rp.search.plugins.IFeedback;
import net.fp.rp.search.plugins.ISearchResult;
import net.fp.rp.search.plugins.events.IInterestedInFeedback;
import net.fp.rp.search.plugins.events.IInterestedInResultsFilter;
import net.fp.rp.search.plugins.events.IInterestedInSearch;
import org.apache.log4j.Logger;
import java.io.File;
import java.util.LinkedList;
import java.util.StringTokenizer;
/**
* Basic intelligence to implement filtering
*
* @author brownpa
* Copyright @link www.firstpartners.net/red
*/
public class BasicIntelligence implements IInterestedInResultsFilter,
IInterestedInFeedback {
/** Default character delimiters */
static final String DELIMITERS = " ";
/** Logger for this class and subclasses */
protected final Logger logger = Logger.getLogger(getClass());
/** Flag to mark that that plugin is ready for feedback calculation */
private boolean readyForFeedbackCalculation;
/** Weight for calculation of the scores for the links */
public double weight;
/** Number of the documents to be searched (top documents) */
public int countTopDocs;
/**
* Constructor of the basic intelligence plugin
*/
public BasicIntelligence() {
readyForFeedbackCalculation = false;
}
/**
* Gives the class a chance to update
*
* @param someInfo
*
* @return
*/
public ISearchResult filterAndSortSearchResults(ISearchResult someInfo) {
return null;
}
/**
* What order we prefer the filter to be called in. lower number = filter
* first
*
* @return int with the preferred filter ord er
*/
public int preferredFilterOrder() {
return 0;
}
/**
* Deal with feedback to improve quality of results next time
*
* @param advice The feedback event
*/
public void acceptFeedback(IFeedback advice) {
//process the feedback according to the type
if (advice instanceof SearchFeedback) {
logger.info(
"Search feedback - background process for score calculation");
//split the query into words
String query = ((SearchFeedback) advice).getFeedbackQuery();
logger.info("Seach Feedback" + query);
//tokenize the query using the default delimiters
StringTokenizer tokenizer = new StringTokenizer(query, DELIMITERS);
try {
//get the plugins interested in search process
IInterestedInSearch[] plugins = PluginManager.getInterestedInSearch();
//links list
LinkedList listLinks = new LinkedList();
//iterate over the found it tokens
while (tokenizer.hasMoreElements()) {
String term = tokenizer.nextToken();
//ignore the terms (conjuction:AND, OR) )
if ((!term.equalsIgnoreCase("and")) &&
(!term.equalsIgnoreCase("or"))) {
//construct the search query for the term
SearchQuery queryForm = new SearchQuery(term);
//for all the plugins invoke the search process
for (int i = 0; i < plugins.length; i++) {
//find the documents that contain the specified token ordered by direct score
plugins[i].doSearch(queryForm,
DocumStruct.FIELD_DIRECTSCORE, true,
countTopDocs,true);
}
//gather the results from all the plugins
for (int i = 0; i < plugins.length; i++) {
ISearchResult result = plugins[i].getResults();
//iterate over all the documents
double sumGroup = 0;
//sum for all the direct score of the nodes found it in the same set
for (int j = 0; j < result.getDocuments().size();
j++) {
DocumStruct doc = (DocumStruct) result.getDocuments()
.get(j);
sumGroup = sumGroup +
PluginManager.getCategoryStores()
.getDirectScore(doc.getCategoryName(),
doc.getId());
}
//calculate the average
double average = 0.0;
if (result.getDocuments().size() > 0) {
average = sumGroup / result.getDocuments().size();
}
//case of "term AND categoryname:X" and set of term < set of categoryname:X (1 category in the system)
//store the new average score for each document in the set
for (int j = 0; j < result.getDocuments().size();
j++) {
DocumStruct doc = (DocumStruct) result.getDocuments()
.get(j);
//create a new document set and it to the list
listLinks.add(new DocumSetStruct(
doc.getCategoryName(), doc.getId(),
average * weight));
}
}
}
}
//get the user feedback score
int score = ((SearchFeedback) advice).getScore();
//update the links and the documents scores
PluginManager.getCategoryStores().updateDocumentLinksAndScore(listLinks,
score);
//optimize the index
PluginManager.getIndexManager().optimize();
} catch (RpException e) {
logger.warn("Exception occured in search feedback process", e);
}
} else if (advice instanceof DocumentFeedback) {
logger.info(
"Document feedback - background process for score calculation");
try {
//convert to feedback document
DocumentFeedback feedback = (DocumentFeedback) advice;
//update the document score
PluginManager.getCategoryStores().updateDocumentDirectScore(feedback.getCategoryName(),
feedback.getDocumentID(), feedback.getScore());
} catch (RpException e) {
logger.warn("Exception occured in document feedback process", e);
}
} else if (advice instanceof CategoryFeedback) {
logger.info(
"Category feedback - background process for score calculation");
try {
//convert to feedback category
CategoryFeedback feedback = (CategoryFeedback) advice;
//update the category score
PluginManager.getCategoryStores().updateCategoryScore(feedback.getCategoryName(),
feedback.getScore());
} catch (RpException e) {
logger.warn("Exception occured in category feedback process", e);
}
}
}
/**
* Called by the PluginManager on each plugin when this class is first
* loaded Read the feedback xml and recalculate the score according to
* this
*
* @throws RpException
*/
public void onLoad() throws RpException {
//when CategoryManager and BasicIndexManager has finished the load process, the flag
//will mark the plugin as ready for feedback recalculation process
logger.info("BasicIntelligence - onLoad process");
final LinkedList feedbackEventsList = new LinkedList();
//get the root folder
String root = PluginManager.getCategoryManager().getRoot();
//get the feedback folder
String feedbackPath = Util.addFolder(root,
BasicFeedbackDataStore.DEFAULT_SPECIAL_DATA);
//get the file list from the folder order by name ascendent
File[] list = Util.getFileList(feedbackPath);
if (list != null) {
//process the feedback files
for (int i = 0; i < list.length; i++) {
//get the file path of the feedback event
String modelpath = list[i].getPath();
logger.info("Load the feedback event from model" + modelpath);
try {
//read the feedback event model and convert it to a base feedback
BaseFeedback feedback = PluginManager.getFeedbackDataStores()
.read(modelpath);
//add the feedback object to the list
feedbackEventsList.add(feedback);
} catch (RpException e) {
logger.warn("Error in reading the feedback event", e);
}
}
} else {
logger.warn("No feedback events in the system");
readyForFeedbackCalculation = true;
}
Thread t = new Thread(new Runnable() {
public void run() {
//wait till the plugin is mark as ready for feedback re-calculation process
while (!readyForFeedbackCalculation);
//if there are feedback events accept it to the system
logger.info("There are " + feedbackEventsList.size() +
" feedback events");
for (int i = 0; i < feedbackEventsList.size(); i++) {
//get the event object
BaseFeedback advice = (BaseFeedback) feedbackEventsList.get(i);
//accept the feedback event
acceptFeedback(advice);
}
}
});
t.setPriority(Thread.MIN_PRIORITY);
t.start();
}
/**
* @return Returns the weight.
*/
public double getWeight() {
return weight;
}
/**
* @param weight The weight to set.
*/
public void setWeight(double weight) {
this.weight = weight;
}
/**
* @return Returns the countTopDocs.
*/
public int getCountTopDocs() {
return countTopDocs;
}
/**
* @param countTopDocs The countTopDocs to set.
*/
public void setCountTopDocs(int countTopDocs) {
this.countTopDocs = countTopDocs;
}
/**
* @return Returns the readyForFeedbackCalculation.
*/
public boolean isReadyForFeedbackCalculation() {
return readyForFeedbackCalculation;
}
/**
* @param readyForFeedbackCalculation The readyForFeedbackCalculation to set.
*/
public void setReadyForFeedbackCalculation(
boolean readyForFeedbackCalculation) {
this.readyForFeedbackCalculation = readyForFeedbackCalculation;
}
}