search completed

This commit is contained in:
Doug Bell 2010-10-11 16:40:09 -05:00
parent 1f3a97235b
commit 0cd851d779
3 changed files with 195 additions and 5 deletions

View file

@ -6,8 +6,10 @@ use Moose;
use JSON qw( from_json to_json );
use namespace::autoclean;
use Scalar::Util;
use Search::QueryParser;
use WebGUI::Pluggable;
use WebGUI::Macro;
use WebGUI::Search;
has 'session' => (
is => 'ro',
@ -133,6 +135,42 @@ sub getAssetTypes {
return %assetList;
}
#----------------------------------------------------------------------
=head2 getKeywordString ( keywordString )
Munge the keyword string from the user into something mysql will Do The
Right Thing with
=cut
# Stolen from WebGUI::Search->search
sub getKeywordString {
my ( $self, $keywords ) = @_;
# do wildcards for people like they'd expect unless they are doing it themselves
unless ($keywords =~ m/"|\*/) {
# split into 'words'. Ideographic characters (such as Chinese) are
# treated as distinct words. Everything else is space delimited.
my @terms = grep { $_ ne q{} } split /\s+|(\p{Ideographic})/, $keywords;
for my $term (@terms) {
# we add padding to ideographic characters to avoid minimum word length limits on indexing
if ($term =~ /\p{Ideographic}/) {
$term = q{''}.$term.q{''};
}
$term .= q{*};
next if WebGUI::Search->_isStopword( $term );
next
if $term =~ /^[+-]/;
$term = q{+} . $term;
}
$keywords = join q{ }, @terms;
}
return $keywords;
}
#----------------------------------------------------------------------
=head2 getNewContentTemplateVars
@ -260,6 +298,80 @@ sub getNewContentTemplateVars {
#----------------------------------------------------------------------------
=head2 getSearchPaginator ( queryString )
Get a paginator for searching with the given queryString.
=cut
sub getSearchPaginator {
my ( $self, $queryString ) = @_;
my $session = $self->session;
my $sql = 'SELECT assetId FROM assetIndex JOIN asset USING (assetId) WHERE '
. ' ( ' . $self->getSqlFromQueryString( $queryString ) . ' ) '
;
my $p = WebGUI::Paginator->new( $session );
$p->setDataByQuery( $sql );
return $p;
}
#----------------------------------------------------------------------------
=head2 getSqlFromQueryString ( queryString )
Parse the query string and return a SQL boolean clause suitable to be used
as a WHERE clause. Does not return WHERE, as you could also use it for HAVING
=cut
sub getSqlFromQueryString {
my ( $self, $queryString ) = @_;
my $sqp = Search::QueryParser->new( defField => 'keywords' );
my $query = $sqp->parse( $queryString );
# Recursion is recursive
my $part = sub {
my ( $query, $conj ) = @_;
my @parts;
for my $part ( @$query ) {
if ( ref $part->{value} ) {
push @parts, $self->getSqlFromQueryString( $_ );
}
elsif ( $part->{field} eq 'keywords' ) {
push @parts, "MATCH ($part->{field}) AGAINST ('"
. $self->getKeywordString( $part->{value} )
. "')";
}
else {
# TODO: Add op validation
# TODO: Add field quoting
# TODO: Add value quoting
if ( $part->{op} eq ':' ) {
my $value = '%' . $part->{value} . '%';
push @parts, "$part->{field} LIKE '$value'";
}
else {
push @parts, "$part->{field} $part->{op} '$part->{value}'"
}
}
}
return join " $conj ", @parts;
};
my $must = $query->{'+'} ? '(' . $part->( $query->{'+'}, 'AND' ) . ')' : undef;
my $mustNot = $query->{'-'} ? 'NOT ( ' . $part->( $query->{'-'}, 'OR' ) . ')' : undef;
my $may = $query->{''} ? $part->( $query->{''}, 'OR' ) : undef;
my $sql = $must . ( $must && $mustNot ? " AND " : '' ) . $mustNot
. ( $must || $mustNot ? " OR " : '' ) . $may
;
return $sql;
}
#----------------------------------------------------------------------------
=head2 getTreePaginator ( $asset )
Get a page for the Asset Tree view. Returns a WebGUI::Paginator object
@ -518,6 +630,64 @@ sub www_processAssetHelper {
#----------------------------------------------------------------------
=head2 www_searchAssets ( )
Search the asset tree for the given keywords and filters
=cut
sub www_searchAssets {
my ( $self ) = @_;
my $session = $self->session;
my ( $user, $form ) = $session->quick(qw{ user form });
# Get the search
my $queryString = $form->get('query');
return to_json( {} ) unless $queryString;
my $i18n = WebGUI::International->new( $session, "Asset" );
my $assetInfo = { assets => [] };
my $p = $self->getSearchPaginator( $queryString );
for my $result ( @{ $p->getPageData } ) {
my $assetId = $result->{assetId};
my $asset = WebGUI::Asset->newById( $session, $assetId );
# Populate the required fields to fill in
my %fields = (
assetId => $asset->getId,
url => $asset->getUrl,
lineage => $asset->lineage,
title => $asset->menuTitle,
revisionDate => $asset->revisionDate,
childCount => $asset->getChildCount,
assetSize => $asset->assetSize,
lockedBy => ($asset->isLockedBy ? $asset->lockedBy->username : ''),
canEdit => $asset->canEdit && $asset->canEditIfLocked,
helpers => $asset->getHelpers,
);
$fields{ className } = {};
# The asset icon
$fields{ icon } = $asset->getIcon("small");
# The asset type (i18n name)
$fields{ className } = $asset->getName;
push @{ $assetInfo->{ assets } }, \%fields;
}
$assetInfo->{ totalAssets } = $p->getRowCount;
$assetInfo->{ sort } = $session->form->get( 'orderByColumn' );
$assetInfo->{ dir } = lc $session->form->get( 'orderByDirection' );
$session->http->setMimeType( 'application/json' );
return to_json( $assetInfo );
}
#----------------------------------------------------------------------
=head2 www_view ( session )
Show the main Admin console wrapper

View file

@ -138,6 +138,10 @@ input.disabled {
margin: 1em 8% 0;
}
.searchResults table {
width: 100%;
}
.searchKeywords {
display: block;
width: 90%;

View file

@ -179,6 +179,10 @@ WebGUI.Admin.prototype.editAsset
*/
WebGUI.Admin.prototype.gotoAsset
= function ( url ) {
if ( this.tabBar.get('activeIndex') > 1 ) {
this.tabBar.selectTab( 0 );
this.currentTab = "view";
}
if ( this.currentTab == "view" ) {
window.frames[ "view" ].location.href = url;
this.treeDirty = 1;
@ -1268,7 +1272,7 @@ WebGUI.Admin.AssetTable.prototype.showHelperMenu
// destroy the old helper menu!
this.helperMenu.destroy();
}
this.helperMenu = new YAHOO.widget.Menu( {
this.helperMenu = new YAHOO.widget.Menu( document.createElement('div'), {
position : "dynamic",
clicktohide : true,
constraintoviewport : true,
@ -1939,11 +1943,13 @@ WebGUI.Admin.Search
// Create a container for the datatable
this.dataTableContainer = document.createElement('div');
this.dataTableContainer.style.display = "none";
this.dataTableContainer.className = "searchResults";
this.dataTableContainer.id = YAHOO.util.Dom.generateId();
this.formContainer.appendChild( this.dataTableContainer );
// Create a container for the paginator
// ...
// TODO
WebGUI.Admin.Search.superclass.constructor.call( this, admin, {
dataSourceUrl : '?op=admin;method=searchAssets;',
dataTableId : this.dataTableContainer.id,
@ -2141,9 +2147,9 @@ WebGUI.Admin.Search.prototype.buildQueryString
WebGUI.Admin.Search.prototype.requestSearch
= function ( ) {
// Build the new search URL
var searchUrl = ';keywords=' + encodeURIComponent( this.searchKeywords.value );
var query = 'query=' + encodeURIComponent( this.searchKeywords.value );
for ( var i = 0; i < this.filters.length; i++ ) {
searchUrl += ';' + filter.type + '=' + filter.getValue();
query += ' ' + encodeURIComponent( filter.type + ':' + filter.getValue() );
}
var callback = {
@ -2157,10 +2163,20 @@ WebGUI.Admin.Search.prototype.requestSearch
this.buildQueryString(
this.dataTable.getState(),
this.dataTable,
searchUrl
query
),
callback
);
};
/**
* onDataReturnInitializeTable ( sRequest, oResponse, oPayload )
* Initialize the table with a new response from the server
*/
WebGUI.Admin.Search.prototype.onDataReturnInitializeTable
= function ( sRequest, oResponse, oPayload ) {
this.dataTableContainer.style.display = "block";
WebGUI.Admin.Tree.superclass.onDataReturnInitializeTable.call( this, sRequest, oResponse, oPayload );
};