From 0c25f245505da154a800cc87f9004553ca73037c Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Fri, 3 Jun 2011 18:51:37 +0000 Subject: [PATCH 1/6] SOLR-2399: Solr Admin Interface, reworked git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1131137 13f79535-47bb-0310-9956-ffa450edef68 --- solr/src/webapp/web/css/screen.css | 105 +- solr/src/webapp/web/index.jsp | 6 +- solr/src/webapp/web/js/script.js | 991 +++++++++++------- solr/src/webapp/web/tpl/analysis.html | 6 + solr/src/webapp/web/tpl/cores.html | 13 +- solr/src/webapp/web/tpl/dashboard.html | 104 +- solr/src/webapp/web/tpl/schema-browser.html | 122 ++- .../webapp/web/tpl/schema-browser_field.html | 109 -- .../webapp/web/tpl/schema-browser_index.html | 11 - 9 files changed, 787 insertions(+), 680 deletions(-) delete mode 100644 solr/src/webapp/web/tpl/schema-browser_field.html delete mode 100644 solr/src/webapp/web/tpl/schema-browser_index.html diff --git a/solr/src/webapp/web/css/screen.css b/solr/src/webapp/web/css/screen.css index fc50d0309de..53e0387f8b1 100644 --- a/solr/src/webapp/web/css/screen.css +++ b/solr/src/webapp/web/css/screen.css @@ -462,6 +462,7 @@ ul #content #dashboard .block { + background-image: none; width: 49%; } @@ -550,85 +551,13 @@ ul display: block; } -#content #dashboard #replication.is-master .slave +#content #dashboard #replication #details table thead td span { display: none; } -#content #dashboard #replication table -{ - border-collapse: collapse; -} - -#content #dashboard #replication table th, -#content #dashboard #replication table td -{ - border: 1px solid #f0f0f0; - padding: 2px 5px; -} - -#content #dashboard #replication table thead td -{ - border: 0; -} - -#content #dashboard #replication table thead th, -#content #dashboard #replication table tbody td -{ - border-right: 0; -} - -#content #dashboard #replication table thead th -{ - border-top: 0; - font-weight: bold; -} - -#content #dashboard #replication table tbody th, -#content #dashboard #replication table tbody td -{ - border-bottom: 0; - text-align: right; -} - -#content #dashboard #replication table tbody th -{ - border-left: 0; -} - -#content #dashboard #replication table tbody th, -#content #dashboard #replication dt -{ - width: 100px; -} - -#content #dashboard #replication dl -{ - display: none; - margin-top: 10px; -} - -#content #dashboard #replication dt, -#content #dashboard #replication dd -{ - display: block; - padding-top: 1px; - padding-bottom: 1px; -} - -#content #dashboard #replication dt -{ - border-right: 1px solid #f0f0f0; - float: left; - padding-left: 5px; - padding-right: 5px; - margin-right: 3px; - text-align: right; -} - #content #dashboard #dataimport { - background-color: #0ff; float: right; } @@ -711,6 +640,19 @@ ul max-width: 99%; } +#content #analysis #analysis-error +{ + background-color: #f00; + background-image: url( ../img/ico/construction.png ); + background-position: 10px 50%; + color: #fff; + display: none; + font-weight: bold; + margin-bottom: 20px; + padding: 10px; + padding-left: 35px; +} + #content #analysis .analysis-result h2 { position: relative; @@ -1334,6 +1276,12 @@ ul padding-left: 10px; } +#content #schema-browser #related #f-df-t +{ + border-bottom: 1px solid #f0f0f0; + padding-bottom: 15px; +} + #content #schema-browser #related dl { margin-top: 15px; @@ -1367,7 +1315,9 @@ ul #content #schema-browser #related .dynamic-field .dynamic-field, #content #schema-browser #related .dynamic-field .dynamic-field a, #content #schema-browser #related .type .type, -#content #schema-browser #related .type .type a +#content #schema-browser #related .type .type a, +#content #schema-browser #related .active, +#content #schema-browser #related .active a { color: #333; } @@ -1378,6 +1328,11 @@ ul color: #666; } +#content #schema-browser #data +{ + display: none; +} + #content #schema-browser #data #index dt { display: none; @@ -1491,6 +1446,7 @@ ul #content #schema-browser #data #field .topterms-holder { + display: none; float: left; } @@ -2830,6 +2786,7 @@ ul #content #replication #details table tbody .size { text-align: right; + white-space: nowrap; } #content #replication #details table tbody .generation div diff --git a/solr/src/webapp/web/index.jsp b/solr/src/webapp/web/index.jsp index dec2ddc4b34..a632b365327 100644 --- a/solr/src/webapp/web/index.jsp +++ b/solr/src/webapp/web/index.jsp @@ -35,14 +35,14 @@

This interface is work in progress. It works best in Chrome.

-

Use the old admin interface if there are problems with this one.

+

Use the old admin interface if there are problems with this one.

Bugs/Requests/Suggestions: SOLR-2399

-

 

+

 

- +
diff --git a/solr/src/webapp/web/js/script.js b/solr/src/webapp/web/js/script.js index 264d88435a7..2b6e33c9aa2 100644 --- a/solr/src/webapp/web/js/script.js +++ b/solr/src/webapp/web/js/script.js @@ -258,7 +258,7 @@ var sammy = $.sammy // #/cores this.get ( - /^#\/cores$/, + /^#\/(cores)$/, function( context ) { sammy.trigger @@ -286,7 +286,7 @@ var sammy = $.sammy // #/cores this.get ( - /^#\/cores\//, + /^#\/(cores)\//, function( context ) { var content_element = $( '#content' ); @@ -386,25 +386,24 @@ var sammy = $.sammy ); var core_names = []; - var core_selects = $( '.swap select', cores_element ); + var core_selects = $( '#actions select', cores_element ); for( var key in cores ) { - core_names.push( '' ) + core_names.push( '' ) } - core_selects .html( core_names.join( "\n") ); - $( 'option[value=' + current_core + ']', core_selects.filter( '.core' ) ) + $( 'option[value="' + current_core + '"]', core_selects.filter( '#swap_core' ) ) .attr( 'selected', 'selected' ); - $( 'option[value=' + current_core + ']', core_selects.filter( '.other' ) ) + $( 'option[value="' + current_core + '"]', core_selects.filter( '.other' ) ) .attr( 'disabled', 'disabled' ) .addClass( 'disabled' ); - $( '.rename input[name=core]', cores_element ) + $( 'input[name="core"]', cores_element ) .val( current_core ); // layout @@ -445,6 +444,57 @@ var sammy = $.sammy } ); + $( 'form a.submit', button_holder_element ) + .die( 'click' ) + .live + ( + 'click', + function( event ) + { + var element = $( this ); + var form_element = element.parents( 'form' ); + var action = $( 'input[name="action"]', form_element ).val().toLowerCase(); + + form_element + .ajaxSubmit + ( + { + url : app.config.solr_path + app.config.core_admin_path + '?wt=json', + dataType : 'json', + beforeSubmit : function( array, form, options ) + { + //loader + }, + success : function( response, status_text, xhr, form ) + { + delete app.cores_data; + + if( 'rename' === action ) + { + context.redirect( path_parts[1] + $( 'input[name="other"]', form_element ).val() ); + } + else if( 'swap' === action ) + { + window.location.reload(); + } + + $( 'a.reset', form ) + .trigger( 'click' ); + }, + error : function( xhr, text_status, error_thrown ) + { + }, + complete : function() + { + //loader + } + } + ); + + return false; + } + ); + $( 'form a.reset', button_holder_element ) .die( 'click' ) .live @@ -452,12 +502,101 @@ var sammy = $.sammy 'click', function( event ) { + $( this ).parents( 'form' ) + .resetForm(); + $( this ).parents( '.button-holder' ) .trigger( 'toggle' ); + + return false; } ); - $( '#actions .optimize', cores_element ) + var reload_button = $( '#actions .reload', cores_element ); + reload_button + .die( 'click' ) + .live + ( + 'click', + function( event ) + { + $.ajax + ( + { + url : app.config.solr_path + app.config.core_admin_path + '?wt=json&action=RELOAD&core=' + current_core, + dataType : 'json', + context : $( this ), + beforeSend : function( xhr, settings ) + { + this + .addClass( 'loader' ); + }, + success : function( response, text_status, xhr ) + { + this + .addClass( 'success' ); + + window.setTimeout + ( + function() + { + reload_button + .removeClass( 'success' ); + }, + 5000 + ); + }, + error : function( xhr, text_status, error_thrown ) + { + }, + complete : function( xhr, text_status ) + { + this + .removeClass( 'loader' ); + } + } + ); + } + ); + + $( '#actions .unload', cores_element ) + .die( 'click' ) + .live + ( + 'click', + function( event ) + { + $.ajax + ( + { + url : app.config.solr_path + app.config.core_admin_path + '?wt=json&action=UNLOAD&core=' + current_core, + dataType : 'json', + context : $( this ), + beforeSend : function( xhr, settings ) + { + this + .addClass( 'loader' ); + }, + success : function( response, text_status, xhr ) + { + delete app.cores_data; + context.redirect( path_parts[1].substr( 0, path_parts[1].length - 1 ) ); + }, + error : function( xhr, text_status, error_thrown ) + { + }, + complete : function( xhr, text_status ) + { + this + .removeClass( 'loader' ); + } + } + ); + } + ); + + var optimize_button = $( '#actions .optimize', cores_element ); + optimize_button .die( 'click' ) .live ( @@ -477,6 +616,19 @@ var sammy = $.sammy }, success : function( response, text_status, xhr ) { + this + .addClass( 'success' ); + + window.setTimeout + ( + function() + { + optimize_button + .removeClass( 'success' ); + }, + 5000 + ); + $( '.optimized dd.ico-0', index_data_element ) .removeClass( 'ico-0' ) .addClass( 'ico-1' ); @@ -1323,19 +1475,44 @@ var sammy = $.sammy 'schema_browser_navi', function( event, params ) { - var related_navigation_element = $( '#related dl', params.schema_browser_element ); + var related_navigation_element = $( '#related dl#f-df-t', params.schema_browser_element ); + var related_navigation_meta = $( '#related dl.ukf-dsf', params.schema_browser_element ); var related_select_element = $( '#related select', params.schema_browser_element ) var type = 'index'; - if( !params.route_params ) + var sammy_basepath = '#/' + $( 'p a', params.active_core ).html() + '/schema-browser'; + + if( !related_navigation_meta.hasClass( 'done' ) ) { - related_navigation_element - .hide(); - - $( 'option:selected', related_select_element ) - .removeAttr( 'selected' ); + if( app.schema_browser_data.unique_key_field ) + { + $( '.unique-key-field', related_navigation_meta ) + .show() + .after + ( + '
' + + app.schema_browser_data.unique_key_field + '
' + ); + } + + if( app.schema_browser_data.default_search_field ) + { + $( '.default-search-field', related_navigation_meta ) + .show() + .after + ( + '
' + + app.schema_browser_data.default_search_field + '
' + ); + } + + related_navigation_meta + .addClass( 'done' ); } - else + + if( params.route_params ) { var type = params.route_params.splat[3]; var value = params.route_params.splat[4]; @@ -1348,7 +1525,7 @@ var sammy = $.sammy 'types' : [] } - $( 'option[value=' + params.route_params.splat[2] + ']', related_select_element ) + $( 'option[value="' + params.route_params.splat[2] + '"]', related_select_element ) .attr( 'selected', 'selected' ); if( 'field' === type ) @@ -1396,7 +1573,6 @@ var sammy = $.sammy } } - var sammy_basepath = '#/' + $( 'p a', params.active_core ).html() + '/schema-browser'; var navigation_content = ''; if( 0 !== navigation_data.fields.length ) @@ -1464,20 +1640,41 @@ var sammy = $.sammy .attr( 'class', type ) .html( navigation_content ); } - - $.get - ( - 'tpl/schema-browser_'+ type + '.html', - function( template ) - { - var data_element = $( '#data', params.schema_browser_element ); + else + { + related_navigation_element + .hide(); - data_element - .html( template ); + $( 'option:selected', related_select_element ) + .removeAttr( 'selected' ); + } - params.callback( app.schema_browser_data, data_element ); - } - ); + if( 'field' === type && value === app.schema_browser_data.unique_key_field ) + { + $( '.unique-key-field', related_navigation_meta ) + .addClass( 'active' ); + } + else + { + $( '.unique-key-field', related_navigation_meta ) + .removeClass( 'active' ); + } + + if( 'field' === type && value === app.schema_browser_data.default_search_field ) + { + $( '.default-search-field', related_navigation_meta ) + .addClass( 'active' ); + } + else + { + $( '.default-search-field', related_navigation_meta ) + .removeClass( 'active' ); + } + + if( params.callback ) + { + params.callback( app.schema_browser_data, $( '#data', params.schema_browser_element ) ); + } } ); @@ -1787,22 +1984,9 @@ var sammy = $.sammy { var callback = function( schema_browser_data, data_element ) { - var sammy_basepath = '#/' + $( 'p a', context.active_core ).html() + '/schema-browser' - - if( schema_browser_data.unique_key_field ) - { - $( '.unique-key-field', data_element ) - .show() - .after( '
' + schema_browser_data.unique_key_field + '
' ); - } - - if( schema_browser_data.default_search_field ) - { - $( '.default-search-field', data_element ) - .show() - .after( '
' + schema_browser_data.default_search_field + '
' ); - } - } + data_element + .hide(); + }; sammy.trigger ( @@ -1815,20 +1999,28 @@ var sammy = $.sammy } ); - // #/:core/schema-browser/field/$field + // #/:core/schema-browser/field|dynamic-field|type/$field this.get ( - /^#\/([\w\d]+)\/(schema-browser)(\/(field)\/(.+))$/, + /^#\/([\w\d]+)\/(schema-browser)(\/(field|dynamic-field|type)\/(.+))$/, function( context ) { var callback = function( schema_browser_data, data_element ) { var field = context.params.splat[4]; + + var type = context.params.splat[3]; + var is_f = 'field' === type; + var is_df = 'dynamic-field' === type; + var is_t = 'type' === type; var options_element = $( '.options', data_element ); - var sammy_basepath = '#/' + $( 'p a', context.active_core ).html() + '/schema-browser' + var sammy_basepath = context.path.indexOf( '/', context.path.indexOf( '/', 2 ) + 1 ); - var keystring_to_list = function( keystring ) + data_element + .show(); + + var keystring_to_list = function( keystring, element_class ) { var key_list = keystring.replace( /-/g, '' ).split( '' ); var list = []; @@ -1849,7 +2041,12 @@ var sammy = $.sammy if( option_key ) { - list.push( '
' + option_key + ',
' ); + list.push + ( + '
' + + option_key + + ',
' + ); } } @@ -1858,192 +2055,265 @@ var sammy = $.sammy return list; } - // -- properties - if( schema_browser_data.fields[field].flags ) + var flags = null; + + if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].flags ) { - var properties_element = $( '.properties', options_element ); - var properties_keys = keystring_to_list( schema_browser_data.fields[field].flags ); + flags = schema_browser_data.fields[field].flags; + } + else if( is_df && schema_browser_data.dynamic_fields[field] && schema_browser_data.dynamic_fields[field].flags ) + { + flags = schema_browser_data.dynamic_fields[field].flags; + } + + // -- properties + var properties_element = $( 'dt.properties', options_element ); + if( flags ) + { + var properties_keys = keystring_to_list( flags, 'properties' ); + + $( 'dd.properties', options_element ) + .remove(); properties_element .show() .after( properties_keys.join( "\n" ) ); } + else + { + $( '.properties', options_element ) + .hide(); + } // -- schema - if( schema_browser_data.fields[field].schema ) + var schema_element = $( 'dt.schema', options_element ); + if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].schema ) { - var schema_element = $( '.schema', options_element ); - var schema_keys = keystring_to_list( schema_browser_data.fields[field].schema ); + var schema_keys = keystring_to_list( schema_browser_data.fields[field].schema, 'schema' ); + + $( 'dd.schema', options_element ) + .remove(); schema_element .show() .after( schema_keys.join( "\n" ) ); } + else + { + $( '.schema', options_element ) + .hide(); + } // -- index - if( schema_browser_data.fields[field].index ) + var index_element = $( 'dt.index', options_element ); + if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].index ) { - var index_element = $( '.index', options_element ); var index_keys = []; if( 0 === schema_browser_data.fields[field].index.indexOf( '(' ) ) { - index_keys.push( '
' + schema_browser_data.fields[field].index + '
' ); + index_keys.push( '
' + schema_browser_data.fields[field].index + '
' ); } else { - index_keys = keystring_to_list( schema_browser_data.fields[field].index ); + index_keys = keystring_to_list( schema_browser_data.fields[field].index, 'index' ); } + $( 'dd.index', options_element ) + .remove(); + index_element .show() .after( index_keys.join( "\n" ) ); } + else + { + $( '.index', options_element ) + .hide(); + } // -- docs - if( schema_browser_data.fields[field].docs ) - { - var docs_element = $( '.docs', options_element ); + var docs_element = $( 'dt.docs', options_element ); + if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].docs ) + { + $( 'dd.docs', options_element ) + .remove(); docs_element .show() - .after( '
' + schema_browser_data.fields[field].docs + '
' ); + .after( '
' + schema_browser_data.fields[field].docs + '
' ); + } + else + { + $( '.docs', options_element ) + .hide(); } // -- distinct - if( schema_browser_data.fields[field].distinct ) + var distinct_element = $( 'dt.distinct', options_element ); + if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].distinct ) { - var distinct_element = $( '.distinct', options_element ); + $( 'dd.distinct', options_element ) + .remove(); distinct_element .show() - .after( '
' + schema_browser_data.fields[field].distinct + '
' ); + .after( '
' + schema_browser_data.fields[field].distinct + '
' ); + } + else + { + $( '.distinct', options_element ) + .hide(); } // -- position-increment-gap - if( schema_browser_data.fields[field].positionIncrementGap ) + var pig_element = $( 'dt.position-increment-gap', options_element ); + if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].positionIncrementGap ) { - var pig_element = $( '.position-increment-gap', options_element ); + $( 'dt.position-increment-gap', options_element ) + .remove(); pig_element .show() - .after( '
' + schema_browser_data.fields[field].positionIncrementGap + '
' ); + .after( '
' + schema_browser_data.fields[field].positionIncrementGap + '
' ); + } + else + { + $( '.position-increment-gap', options_element ) + .hide(); + } + + var analyzer_element = $( '.analyzer', data_element ); + var analyzer_data = null; + + if( is_f ) + { + analyzer_data = schema_browser_data.types[schema_browser_data.relations.f_t[field]]; + } + else if( is_df ) + { + analyzer_data = schema_browser_data.types[schema_browser_data.relations.df_t[field]]; + } + else if( is_t ) + { + analyzer_data = schema_browser_data.types[field]; } - var analyzer_data = schema_browser_data.types[schema_browser_data.relations.f_t[field]]; - var analyzer_element = $( '.analyzer', data_element ); - - var transform_analyzer_data_into_list = function( analyzer_data ) + if( analyzer_data ) { - var args = []; - for( var key in analyzer_data.args ) + var transform_analyzer_data_into_list = function( analyzer_data ) { - var arg_class = ''; - var arg_content = ''; + var args = []; + for( var key in analyzer_data.args ) + { + var arg_class = ''; + var arg_content = ''; - if( 'true' === analyzer_data.args[key] || '1' === analyzer_data.args[key] ) - { - arg_class = 'ico-1'; - arg_content = key; - } - else if( 'false' === analyzer_data.args[key] || '0' === analyzer_data.args[key] ) - { - arg_class = 'ico-0'; - arg_content = key; - } - else - { - arg_content = key + ': '; - - if( 'synonyms' === key || 'words' === key ) + if( 'true' === analyzer_data.args[key] || '1' === analyzer_data.args[key] ) { - // @TODO: set link target for file - arg_content += '' + analyzer_data.args[key] + ''; + arg_class = 'ico-1'; + arg_content = key; + } + else if( 'false' === analyzer_data.args[key] || '0' === analyzer_data.args[key] ) + { + arg_class = 'ico-0'; + arg_content = key; } else { - arg_content += analyzer_data.args[key]; + arg_content = key + ': '; + + if( 'synonyms' === key || 'words' === key ) + { + // @TODO: set link target for file + arg_content += '' + analyzer_data.args[key] + ''; + } + else + { + arg_content += analyzer_data.args[key]; + } } + + args.push( '
' + arg_content + '
' ); } - args.push( '
' + arg_content + '
' ); + var list_content = '
' + analyzer_data.className + '
'; + if( 0 !== args.length ) + { + args.sort(); + list_content += args.join( "\n" ); + } + + return list_content; } - var list_content = '
' + analyzer_data.className + '
'; - if( 0 !== args.length ) + // -- field-type + var field_type_element = $( 'dt.field-type', options_element ); + + $( 'dd.field-type', options_element ) + .remove(); + + field_type_element + .show() + .after( '
' + analyzer_data.className + '
' ); + + + for( var key in analyzer_data ) { - args.sort(); - list_content += args.join( "\n" ); - } - - return list_content; - } - - // -- field-type - var field_type_element = $( '.field-type', options_element ); - - field_type_element - .show() - .after( '
' + analyzer_data.className + '
' ); - - - for( var key in analyzer_data ) - { - var key_match = key.match( /^(.+)Analyzer$/ ); - if( !key_match ) - { - continue; - } - - var analyzer_key_element = $( '.' + key_match[1], analyzer_element ); - var analyzer_key_data = analyzer_data[key]; - - analyzer_element.show(); - analyzer_key_element.show(); - - if( analyzer_key_data.className ) - { - $( 'dl:first dt', analyzer_key_element ) - .html( analyzer_key_data.className ); - } - - for( var type in analyzer_key_data ) - { - if( 'object' !== typeof analyzer_key_data[type] ) + var key_match = key.match( /^(.+)Analyzer$/ ); + if( !key_match ) { continue; } - var type_element = $( '.' + type, analyzer_key_element ); - var type_content = []; + var analyzer_key_element = $( '.' + key_match[1], analyzer_element ); + var analyzer_key_data = analyzer_data[key]; - type_element.show(); + analyzer_element.show(); + analyzer_key_element.show(); - if( analyzer_key_data[type].className ) + if( analyzer_key_data.className ) { - type_content.push( transform_analyzer_data_into_list( analyzer_key_data[type] ) ); + $( 'dl:first dt', analyzer_key_element ) + .html( analyzer_key_data.className ); } - else + + $( 'ul li', analyzer_key_element ) + .hide(); + + for( var type in analyzer_key_data ) { - for( var entry in analyzer_key_data[type] ) + if( 'object' !== typeof analyzer_key_data[type] ) { - type_content.push( transform_analyzer_data_into_list( analyzer_key_data[type][entry] ) ); + continue; } - } - $( 'dl', type_element ) - .append( type_content.join( "\n" ) ); + var type_element = $( '.' + type, analyzer_key_element ); + var type_content = []; + + type_element.show(); + + if( analyzer_key_data[type].className ) + { + type_content.push( transform_analyzer_data_into_list( analyzer_key_data[type] ) ); + } + else + { + for( var entry in analyzer_key_data[type] ) + { + type_content.push( transform_analyzer_data_into_list( analyzer_key_data[type][entry] ) ); + } + } + + $( 'dl', type_element ) + .empty() + .append( type_content.join( "\n" ) ); + } } } - var topterms_holder_element = $( '.topterms-holder', data_element ); - if( !schema_browser_data.fields[field].topTerms_hash ) - { - topterms_holder_element - .hide(); - } - else + if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].topTerms_hash ) { topterms_holder_element .show(); @@ -2077,6 +2347,7 @@ var sammy = $.sammy topterms_content += ''; topterms_table_element + .empty() .append( topterms_content ); $( 'tbody', topterms_table_element ) @@ -2139,14 +2410,14 @@ var sammy = $.sammy } ); } - - var histogram_holder_element = $( '.histogram-holder', data_element ); - if( !schema_browser_data.fields[field].histogram_hash ) + else { - histogram_holder_element + topterms_holder_element .hide(); } - else + + var histogram_holder_element = $( '.histogram-holder', data_element ); + if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].histogram_hash ) { histogram_holder_element .show(); @@ -2184,52 +2455,11 @@ var sammy = $.sammy } ); } - } - - sammy.trigger - ( - 'schema_browser_load', + else { - callback : callback, - active_core : this.active_core, - route_params : this.params + histogram_holder_element + .hide(); } - ); - } - ); - - // #/:core/schema-browser/dynamic-field/$field - this.get - ( - /^#\/([\w\d]+)\/(schema-browser)(\/(dynamic-field)\/(.+))$/, - function( context ) - { - var callback = function( schema_browser_data, data_element ) - { - console.debug( data_element ); - } - - sammy.trigger - ( - 'schema_browser_load', - { - callback : callback, - active_core : this.active_core, - route_params : this.params - } - ); - } - ); - - // #/:core/schema-browser/type/$type - this.get - ( - /^#\/([\w\d]+)\/(schema-browser)(\/(type)\/(.+))$/, - function( context ) - { - var callback = function( schema_browser_data, data_element ) - { - console.debug( data_element ); } sammy.trigger @@ -2287,7 +2517,7 @@ var sammy = $.sammy // #/:core/dataimport this.get ( - /^#\/([\w\d]+)\/dataimport$/, + /^#\/([\w\d]+)\/(dataimport)$/, function( context ) { sammy.trigger @@ -2297,6 +2527,14 @@ var sammy = $.sammy active_core : this.active_core, callback : function( dataimport_handlers ) { + if( 0 === dataimport_handlers.length ) + { + $( '#content' ) + .html( 'sorry, no dataimport-handler defined!' ); + + return false; + } + context.redirect( context.path + '/' + dataimport_handlers[0] ); } } @@ -2307,7 +2545,7 @@ var sammy = $.sammy // #/:core/dataimport this.get ( - /^#\/([\w\d]+)\/dataimport\//, + /^#\/([\w\d]+)\/(dataimport)\//, function( context ) { var core_basepath = this.active_core.attr( 'data-basepath' ); @@ -2341,6 +2579,7 @@ var sammy = $.sammy active_core : context.active_core, callback : function( dataimport_handlers ) { + var handlers_element = $( '.handler', form_element ); var handlers = []; @@ -2357,7 +2596,7 @@ var sammy = $.sammy $( 'ul', handlers_element ) .html( handlers.join( "\n") ) ; - $( 'a[href=' + context.path + ']', handlers_element ).parent() + $( 'a[href="' + context.path + '"]', handlers_element ).parent() .addClass( 'active' ); handlers_element @@ -2529,20 +2768,11 @@ var sammy = $.sammy { started_at = (new Date()).toGMTString(); } - console.debug( 'started_at @ ', started_at ); function dataimport_compute_details( response, details_element ) { var details = []; - - console.debug( 'elapsed @ ', $( 'str[name="Time Elapsed"]', response ).text() ); - console.debug( 'taken @ ', $( 'str[name="Time taken "]', response ).text() ); - console.debug( 'requests @ ', $( 'str[name="Total Requests made to DataSource"]', response ).text() ); - console.debug( 'fetched @ ', $( 'str[name="Total Rows Fetched"]', response ).text() ); - console.debug( 'skipped @ ', $( 'str[name="Total Documents Skipped"]', response ).text() ); - console.debug( 'processed @ ', $( 'str[name="Total Documents Processed"]', response ).text() ); - var requests = parseInt( $( 'str[name="Total Requests made to DataSource"]', response ).text() ); if( NaN !== requests ) { @@ -2604,7 +2834,6 @@ var sammy = $.sammy $( '.info strong', state_element ) .text( $( 'str[name=""]', response ).text() ); - console.debug( 'failure' ); console.debug( 'rollback @ ', rollback_element.text() ); } else if( 'idle' === status && 0 !== messages_count ) @@ -2620,7 +2849,6 @@ var sammy = $.sammy $( '.info strong', state_element ) .text( $( 'str[name=""]', response ).text() ); - console.debug( 'success' ); dataimport_compute_details( response, $( '.info .details', state_element ) ); } else if( 'busy' === status ) @@ -2639,7 +2867,6 @@ var sammy = $.sammy $( '.info strong', state_element ) .text( 'Indexing ...' ); - console.debug( 'indexing' ); dataimport_compute_details( response, $( '.info .details', state_element ) ); window.setTimeout( dataimport_fetch_status, 2000 ); @@ -2781,7 +3008,7 @@ var sammy = $.sammy content += '
' + "\n"; content += '

' + key + '

' + "\n"; content += '
' + "\n"; - content += '
    ' + "\n"; + content += '
      '; for( var sort_key in sort_table[key] ) { @@ -2863,6 +3090,16 @@ var sammy = $.sammy } ); + $( '.block .content > ul:empty', this ) + .each + ( + function( index, element ) + { + $( element ).parents( '.block' ) + .hide(); + } + ); + $( '.entry', this ) .each ( @@ -3005,15 +3242,12 @@ var sammy = $.sammy // #/:core/analysis this.get ( - /^#\/([\w\d]+)\/analysis$/, + /^#\/([\w\d]+)\/(analysis)$/, function( context ) { var core_basepath = this.active_core.attr( 'data-basepath' ); var content_element = $( '#content' ); - $( 'li.analysis', this.active_core ) - .addClass( 'active' ); - $.get ( 'tpl/analysis.html', @@ -3073,8 +3307,8 @@ var sammy = $.sammy this .html( content ); - - $( 'option[value=fieldname\=' + response.schema.defaultSearchField + ']', this ) + + $( 'option[value="fieldname\=' + response.schema.defaultSearchField + '"]', this ) .attr( 'selected', 'selected' ); }, error : function( xhr, text_status, error_thrown) @@ -3123,6 +3357,11 @@ var sammy = $.sammy build_analysis_table( 'type', name, response.analysis.field_types[name] ); } }, + error : function( xhr, text_status, error_thrown ) + { + $( '#analysis-error', analysis_element ) + .show(); + }, complete : function() { //loader @@ -3318,70 +3557,7 @@ var sammy = $.sammy .html( template ); var dashboard_element = $( '#dashboard' ); - - /* - $.ajax - ( - { - url : core_basepath + '/admin/system?wt=json', - dataType : 'json', - context : $( '#system', dashboard_element ), - beforeSend : function( xhr, settings ) - { - $( 'h2', this ) - .addClass( 'loader' ); - - $( '.message', this ) - .show() - .html( 'Loading' ); - }, - success : function( response, text_status, xhr ) - { - $( '.message', this ) - .empty() - .hide(); - - $( 'dl', this ) - .show(); - - var data = { - 'core_now' : response['core']['now'], - 'core_start' : response['core']['start'], - 'core_host' : response['core']['host'], - 'core_schema' : response['core']['schema'], - 'lucene_solr-spec-version' : response['lucene']['solr-spec-version'], - 'lucene_solr-impl-version' : response['lucene']['solr-impl-version'], - 'lucene_lucene-spec-version' : response['lucene']['lucene-spec-version'], - 'lucene_lucene-impl-version' : response['lucene']['lucene-impl-version'] - }; - - for( var key in data ) - { - $( '.' + key, this ) - .show(); - - $( '.value.' + key, this ) - .html( data[key] ); - } - }, - error : function( xhr, text_status, error_thrown) - { - this - .addClass( 'disabled' ); - - $( '.message', this ) - .show() - .html( 'System-Handler is not configured' ); - }, - complete : function( xhr, text_status ) - { - $( 'h2', this ) - .removeClass( 'loader' ); - } - } - ); - //*/ - + $.ajax ( { @@ -3515,7 +3691,7 @@ var sammy = $.sammy $( '.timeago', this ) .timeago(); }, - error : function( xhr, text_status, error_thrown) + error : function( xhr, text_status, error_thrown ) { this .addClass( 'disabled' ); @@ -3562,68 +3738,68 @@ var sammy = $.sammy $( '.replication', context.active_core ) .show(); - var is_master = 'undefined' === typeof( response['details']['slave'] ); + var data = response.details; + var is_slave = 'undefined' !== typeof( data.slave ); var headline = $( 'h2 span', this ); + var details_element = $( '#details', this ); + var current_type_element = $( ( is_slave ? '.slave' : '.master' ), this ); - if( is_master ) + if( is_slave ) { this - .addClass( 'is-master' ); - - headline - .html( headline.html() + ' (Master)' ); - } - else - { - this - .addClass( 'is-slave' ); + .addClass( 'slave' ); headline .html( headline.html() + ' (Slave)' ); } - - var data = { - 'details_index-version' : response['details']['indexVersion'], - 'details_generation' : response['details']['generation'], - 'details_index-size' : response['details']['indexSize'] - }; - - if( !is_master ) + else { - $.extend - ( - data, - { - 'details_slave_master-details_index-version' : response['details']['slave']['masterDetails']['indexVersion'], - 'details_slave_master-details_generation' : response['details']['slave']['masterDetails']['generation'], - 'details_slave_master-details_index-size' : response['details']['slave']['masterDetails']['indexSize'], - 'details_slave_master-url' : response['details']['slave']['masterUrl'], - 'details_slave_poll-interval' : response['details']['slave']['pollInterval'], - 'details_slave_next-execution-at' : response['details']['slave']['nextExecutionAt'], - 'details_slave_index-replicated-at' : response['details']['slave']['indexReplicatedAt'], - 'details_slave_last-cycle-bytes-downloaded' : response['details']['slave']['lastCycleBytesDownloaded'], - 'details_slave_replication-failed-at' : response['details']['slave']['replicationFailedAt'], - 'details_slave_previous-cycle-time-in-seconds' : response['details']['slave']['previousCycleTimeInSeconds'], - 'details_slave_is-polling-disabled' : response['details']['slave']['isPollingDisabled'], - 'details_slave_is-replicating' : response['details']['slave']['isReplicating'] - } - ); - - $( 'dl', this ) - .show(); - } - - for( var key in data ) - { - $( '.' + key, this ) - .show(); + this + .addClass( 'master' ); - $( '.value.' + key, this ) - .html( data[key] ); + headline + .html( headline.html() + ' (Master)' ); } - // $( '.timeago', this ) - // .timeago(); + $( '.version div', current_type_element ) + .html( data.indexVersion ); + $( '.generation div', current_type_element ) + .html( data.generation ); + $( '.size div', current_type_element ) + .html( data.indexSize ); + + if( is_slave ) + { + var master_element = $( '.master', details_element ); + $( '.version div', master_element ) + .html( data.slave.masterDetails.indexVersion ); + $( '.generation div', master_element ) + .html( data.slave.masterDetails.generation ); + $( '.size div', master_element ) + .html( data.slave.masterDetails.indexSize ); + + if( data.indexVersion !== data.slave.masterDetails.indexVersion ) + { + $( '.version', details_element ) + .addClass( 'diff' ); + } + else + { + $( '.version', details_element ) + .removeClass( 'diff' ); + } + + if( data.generation !== data.slave.masterDetails.generation ) + { + $( '.generation', details_element ) + .addClass( 'diff' ); + } + else + { + $( '.generation', details_element ) + .removeClass( 'diff' ); + } + } }, error : function( xhr, text_status, error_thrown) { @@ -3642,7 +3818,6 @@ var sammy = $.sammy } ); - /* $.ajax ( { @@ -3688,7 +3863,7 @@ var sammy = $.sammy $( '.message', this ) .show() - .html( 'DataImport is not configured' ); + .html( 'Dataimport is not configured' ); }, complete : function( xhr, text_status ) { @@ -3697,7 +3872,6 @@ var sammy = $.sammy } } ); - //*/ $.ajax ( @@ -3824,32 +3998,37 @@ var sammy = $.sammy .show(); } - var cmd_arg_key_element = $( '.command_line_args dt', this ); - var cmd_arg_element = $( '.command_line_args dd', this ); - - for( var key in app.dashboard_values['jvm']['jmx']['commandLineArgs'] ) + var commandLineArgs = app.dashboard_values['jvm']['jmx']['commandLineArgs']; + if( 0 !== commandLineArgs.length ) { - cmd_arg_element = cmd_arg_element.clone(); - cmd_arg_element.html( app.dashboard_values['jvm']['jmx']['commandLineArgs'][key] ); + var cmd_arg_element = $( '.command_line_args dt', this ); + var cmd_arg_key_element = $( '.command_line_args dt', this ); + var cmd_arg_element = $( '.command_line_args dd', this ); - cmd_arg_key_element - .after( cmd_arg_element ); + for( var key in commandLineArgs ) + { + cmd_arg_element = cmd_arg_element.clone(); + cmd_arg_element.html( commandLineArgs[key] ); + + cmd_arg_key_element + .after( cmd_arg_element ); + } + + cmd_arg_key_element.closest( 'li' ) + .show(); + + $( '.command_line_args dd:last', this ) + .remove(); + + $( '.command_line_args dd:odd', this ) + .addClass( 'odd' ); } - cmd_arg_key_element.closest( 'li' ) - .show(); - - $( '.command_line_args dd:last', this ) - .remove(); - $( '.timeago', this ) .timeago(); $( 'li:visible:odd', this ) .addClass( 'odd' ); - - $( '.command_line_args dd:odd', this ) - .addClass( 'odd' ); // -- memory bar @@ -3912,7 +4091,7 @@ var sammy = $.sammy } ); -var solr_admin = function() +var solr_admin = function( app_config ) { menu_element = null, @@ -3921,14 +4100,14 @@ var solr_admin = function() active_core = null, environment_basepath = null, - config = null, + config = app_config, params = null, dashboard_values = null, schema_browser_data = null, this.init_menu = function() { - $( '.ping a', this.menu_element ) + $( '.ping a', menu_element ) .live ( 'click', @@ -3943,7 +4122,7 @@ var solr_admin = function() } ); - $( 'a[rel]', this.menu_element ) + $( 'a[rel]', menu_element ) .live ( 'click', @@ -3954,30 +4133,13 @@ var solr_admin = function() } ); } - - this.__construct = function() - { - this.menu_element = $( '#menu ul' ); - - this.init_menu(); - } - this.__construct(); -} -var app; -$( document ).ready -( - function() + this.init_cores = function() { - jQuery.timeago.settings.allowFuture = true; - - app = new solr_admin(); - app.config = app_config; - $.ajax ( { - url : app.config.solr_path + app.config.core_admin_path + '?wt=json', + url : config.solr_path + config.core_admin_path + '?wt=json', dataType : 'json', beforeSend : function( arr, form, options ) { @@ -3986,28 +4148,28 @@ $( document ).ready }, success : function( response ) { - app.cores_data = response.status; - app.is_multicore = 'undefined' === typeof response.status['']; + this.cores_data = response.status; + is_multicore = 'undefined' === typeof response.status['']; - if( app.is_multicore ) + if( is_multicore ) { - $( '#cores', app.menu_element ) + $( '#cores', menu_element ) .show(); } for( var core_name in response.status ) { - var core_path = app.config.solr_path + '/' + core_name; + var core_path = config.solr_path + '/' + core_name; if( !core_name ) { core_name = 'singlecore'; - core_path = app.config.solr_path + core_path = config.solr_path } - if( !app.environment_basepath ) + if( !environment_basepath ) { - app.environment_basepath = core_path; + environment_basepath = core_path; } var core_tpl = '
    • ' + "\n" @@ -4017,32 +4179,32 @@ $( document ).ready + '
    • Query
    • ' + "\n" + '
    • Schema
    • ' + "\n" + '
    • Config
    • ' + "\n" - + '
    • Replication
    • ' + "\n" - + '
    • Analysis
    • ' + "\n" - + '
    • Schema Browser
    • ' + "\n" - + '
    • Statistics
    • ' + "\n" + + '
    • Replication
    • ' + "\n" + + '
    • Analysis
    • ' + "\n" + + '
    • Schema Browser
    • ' + "\n" + + '
    • Statistics
    • ' + "\n" + '
    • Ping
    • ' + "\n" - + '
    • Plugins
    • ' + "\n" + + '
    • Plugins
    • ' + "\n" + '
    • Dataimport
    • ' + "\n" + '
    ' + "\n" + ''; - app.menu_element + menu_element .append( core_tpl ); } $.ajax ( { - url : app.environment_basepath + '/admin/system?wt=json', + url : environment_basepath + '/admin/system?wt=json', dataType : 'json', beforeSend : function( arr, form, options ) { }, success : function( response ) { - app.dashboard_values = response; + dashboard_values = response; var environment_args = null; var cloud_args = null; @@ -4116,4 +4278,27 @@ $( document ).ready } ); } + + this.__construct = function() + { + menu_element = $( '#menu ul' ); + + this.init_menu(); + this.init_cores(); + + this.menu_element = menu_element; + this.config = config; + } + this.__construct(); +} + +var app; +$( document ).ready +( + function() + { + jQuery.timeago.settings.allowFuture = true; + + app = new solr_admin( app_config ); + } ); \ No newline at end of file diff --git a/solr/src/webapp/web/tpl/analysis.html b/solr/src/webapp/web/tpl/analysis.html index a1271c9b4c5..8f250a55b71 100644 --- a/solr/src/webapp/web/tpl/analysis.html +++ b/solr/src/webapp/web/tpl/analysis.html @@ -1,5 +1,11 @@
    +
    + + This Functionality requires the /analysis/field Handler to be registered and active! + +
    +

    Field Analysis

    diff --git a/solr/src/webapp/web/tpl/cores.html b/solr/src/webapp/web/tpl/cores.html index fa5cd16e28d..5baf4bb2a86 100644 --- a/solr/src/webapp/web/tpl/cores.html +++ b/solr/src/webapp/web/tpl/cores.html @@ -18,8 +18,10 @@
    + +

    -

    +

    @@ -42,12 +44,15 @@ + + +

    -

    -

    @@ -181,6 +186,8 @@ + +

    diff --git a/solr/src/webapp/web/tpl/dashboard.html b/solr/src/webapp/web/tpl/dashboard.html index 0a2556099dd..7c9997197ba 100644 --- a/solr/src/webapp/web/tpl/dashboard.html +++ b/solr/src/webapp/web/tpl/dashboard.html @@ -63,96 +63,52 @@
    -
    - +
    + + + - - - + + + + + + + - - - - + + + + + + + + - - - - - - - - - + + + + + + + + + +
     slavemasterIndexVersionGenSize
    indexVersion
    Master:
    x
    y
    z
    generation
    indexSize
    Slave:
    a
    c
    c
    - -
    - -
    masterUrl
    -
    - -
    poll every
    -
    - -
    last replicated
    -
    - -
    replicate next
    -
    - -
    last failed
    -
    - -
    - -
    -

    DataImport-Handler

    +

    Dataimport

    diff --git a/solr/src/webapp/web/tpl/schema-browser.html b/solr/src/webapp/web/tpl/schema-browser.html index a5a6154373e..592c8547981 100644 --- a/solr/src/webapp/web/tpl/schema-browser.html +++ b/solr/src/webapp/web/tpl/schema-browser.html @@ -4,19 +4,135 @@
    - #data +
    + +
    + +
    + +
    Field-Type:
    + +
    Properties:
    + +
    Schema:
    + +
    Index:
    + +
    PI Gap:
    + +
    Docs:
    + +
    Distinct:
    + +
    + +
      +
    • + +

      Index Analyzer:

      +
      +
      +
      + +
        +
      • +

        Tokenizer:

        +
        +
        +
      • +
      • +

        Filters:

        +
        +
        +
      • +
      + +
    • +
    • + +

      Query Analyzer:

      +
      +
      +
      + +
        +
      • +

        Tokenizer:

        +
        +
        +
      • +
      • +

        Filters:

        +
        +
        +
      • +
      + +
    • +
    + +
    + +
    + +

    Top / Terms:

    + + + + + + + + + + + + + + + +
     TermFrq
    + + + +
    + +
    + +

    Histogram:

    + +
    + +
    + +
    + +
    + +
    diff --git a/solr/src/webapp/web/tpl/schema-browser_field.html b/solr/src/webapp/web/tpl/schema-browser_field.html deleted file mode 100644 index 1cbc3a6e589..00000000000 --- a/solr/src/webapp/web/tpl/schema-browser_field.html +++ /dev/null @@ -1,109 +0,0 @@ -
    - -
    - -
    - -
    Field-Type:
    - -
    Properties:
    - -
    Schema:
    - -
    Index:
    - -
    PI Gap:
    - -
    Docs:
    - -
    Distinct:
    - -
    - -
      -
    • - -

      Index Analyzer:

      -
      -
      -
      - -
        -
      • -

        Tokenizer:

        -
        -
        -
      • -
      • -

        Filters:

        -
        -
        -
      • -
      - -
    • -
    • - -

      Query Analyzer:

      -
      -
      -
      - -
        -
      • -

        Tokenizer:

        -
        -
        -
      • -
      • -

        Filters:

        -
        -
        -
      • -
      - -
    • -
    - -
    - -
    - -

    Top / Terms:

    - - - - - - - - - - - - - - - -
     TermFrq
    - - - -
    - -
    - -

    Histogram:

    - -
    - -
    - -
    - -
    - -
    \ No newline at end of file diff --git a/solr/src/webapp/web/tpl/schema-browser_index.html b/solr/src/webapp/web/tpl/schema-browser_index.html deleted file mode 100644 index f872f11f5ec..00000000000 --- a/solr/src/webapp/web/tpl/schema-browser_index.html +++ /dev/null @@ -1,11 +0,0 @@ -
    - -
    - -
    Unique Key Field:
    - -
    Default Search Field:
    - -
    - -
    \ No newline at end of file From 27d8311ffcfc02d218e0e435c457a6daa348a2cf Mon Sep 17 00:00:00 2001 From: Michael McCandless Date: Fri, 3 Jun 2011 19:17:40 +0000 Subject: [PATCH 2/6] rename to test to match the class it's testing git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1131150 13f79535-47bb-0310-9956-ffa450edef68 --- ...GroupsCollectorTest.java => TermAllGroupsCollectorTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename modules/grouping/src/test/org/apache/lucene/search/grouping/{AllGroupsCollectorTest.java => TermAllGroupsCollectorTest.java} (98%) diff --git a/modules/grouping/src/test/org/apache/lucene/search/grouping/AllGroupsCollectorTest.java b/modules/grouping/src/test/org/apache/lucene/search/grouping/TermAllGroupsCollectorTest.java similarity index 98% rename from modules/grouping/src/test/org/apache/lucene/search/grouping/AllGroupsCollectorTest.java rename to modules/grouping/src/test/org/apache/lucene/search/grouping/TermAllGroupsCollectorTest.java index cff33202df3..0e6004e0696 100644 --- a/modules/grouping/src/test/org/apache/lucene/search/grouping/AllGroupsCollectorTest.java +++ b/modules/grouping/src/test/org/apache/lucene/search/grouping/TermAllGroupsCollectorTest.java @@ -27,7 +27,7 @@ import org.apache.lucene.search.TermQuery; import org.apache.lucene.store.Directory; import org.apache.lucene.util.LuceneTestCase; -public class AllGroupsCollectorTest extends LuceneTestCase { +public class TermAllGroupsCollectorTest extends LuceneTestCase { public void testTotalGroupCount() throws Exception { From 01c8469ab3350bf5c879ac84c7726344b8d1863c Mon Sep 17 00:00:00 2001 From: Michael McCandless Date: Fri, 3 Jun 2011 19:31:51 +0000 Subject: [PATCH 3/6] LUCENE-3129: BlockGroupingCollector wasn't tracking scores correctly; fang'd up TestGrouping to reveal the bug git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1131158 13f79535-47bb-0310-9956-ffa450edef68 --- .../lucene/index/TermsHashPerField.java | 6 +- .../grouping/BlockGroupingCollector.java | 2 +- .../TermFirstPassGroupingCollector.java | 2 +- .../TermSecondPassGroupingCollector.java | 2 +- .../lucene/search/grouping/TestGrouping.java | 227 ++++++++++++++---- 5 files changed, 187 insertions(+), 52 deletions(-) diff --git a/lucene/src/java/org/apache/lucene/index/TermsHashPerField.java b/lucene/src/java/org/apache/lucene/index/TermsHashPerField.java index f3d705e4433..1e8df8beef6 100644 --- a/lucene/src/java/org/apache/lucene/index/TermsHashPerField.java +++ b/lucene/src/java/org/apache/lucene/index/TermsHashPerField.java @@ -181,9 +181,9 @@ final class TermsHashPerField extends InvertedDocConsumerPerField { // term text into textStart address // Get the text & hash of this term. int termID; - try{ - termID = bytesHash.add(termBytesRef, termAtt.fillBytesRef()); - }catch (MaxBytesLengthExceededException e) { + try { + termID = bytesHash.add(termBytesRef, termAtt.fillBytesRef()); + } catch (MaxBytesLengthExceededException e) { // Not enough room in current block // Just skip this term, to remain as robust as // possible during indexing. A TokenFilter diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java index 06a7c988452..7eb26fd9617 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java @@ -212,7 +212,7 @@ public class BlockGroupingCollector extends Collector { // Swap pending scores final float[] savScores = og.scores; og.scores = pendingSubScores; - pendingSubScores = og.scores; + pendingSubScores = savScores; } og.readerContext = currentReaderContext; //og.groupOrd = lastGroupOrd; diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/TermFirstPassGroupingCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/TermFirstPassGroupingCollector.java index d194d0ed1d0..2ac341fc2d6 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/TermFirstPassGroupingCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/TermFirstPassGroupingCollector.java @@ -26,7 +26,7 @@ import java.io.IOException; /** * Concrete implementation of {@link AbstractFirstPassGroupingCollector} that groups based on - * field values and more specifically uses {@link org.apache.lucene.search.FieldCache.DocTerms} + * field values and more specifically uses {@link org.apache.lucene.search.FieldCache.DocTermsIndex} * to collect groups. * * @lucene.experimental diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/TermSecondPassGroupingCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/TermSecondPassGroupingCollector.java index 40d91b811a7..bf81f98ed90 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/TermSecondPassGroupingCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/TermSecondPassGroupingCollector.java @@ -27,7 +27,7 @@ import java.util.Collection; /** * Concrete implementation of {@link AbstractSecondPassGroupingCollector} that groups based on - * field values and more specifically uses {@link org.apache.lucene.search.FieldCache.DocTerms} + * field values and more specifically uses {@link org.apache.lucene.search.FieldCache.DocTermsIndex} * to collect grouped docs. * * @lucene.experimental diff --git a/modules/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java b/modules/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java index 87745f64a18..2a4bcbcec61 100644 --- a/modules/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java +++ b/modules/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java @@ -154,7 +154,10 @@ public class TestGrouping extends LuceneTestCase { final BytesRef group; final BytesRef sort1; final BytesRef sort2; + // content must be "realN ..." final String content; + float score; + float score2; public GroupDoc(int id, BytesRef group, BytesRef sort1, BytesRef sort2, String content) { this.id = id; @@ -167,16 +170,21 @@ public class TestGrouping extends LuceneTestCase { private Sort getRandomSort() { final List sortFields = new ArrayList(); - if (random.nextBoolean()) { + if (random.nextInt(7) == 2) { + sortFields.add(SortField.FIELD_SCORE); + } else { if (random.nextBoolean()) { + if (random.nextBoolean()) { + sortFields.add(new SortField("sort1", SortField.STRING, random.nextBoolean())); + } else { + sortFields.add(new SortField("sort2", SortField.STRING, random.nextBoolean())); + } + } else if (random.nextBoolean()) { sortFields.add(new SortField("sort1", SortField.STRING, random.nextBoolean())); - } else { sortFields.add(new SortField("sort2", SortField.STRING, random.nextBoolean())); } - } else if (random.nextBoolean()) { - sortFields.add(new SortField("sort1", SortField.STRING, random.nextBoolean())); - sortFields.add(new SortField("sort2", SortField.STRING, random.nextBoolean())); } + // Break ties: sortFields.add(new SortField("id", SortField.INT)); return new Sort(sortFields.toArray(new SortField[sortFields.size()])); } @@ -188,7 +196,15 @@ public class TestGrouping extends LuceneTestCase { public int compare(GroupDoc d1, GroupDoc d2) { for(SortField sf : sortFields) { final int cmp; - if (sf.getField().equals("sort1")) { + if (sf.getType() == SortField.SCORE) { + if (d1.score > d2.score) { + cmp = -1; + } else if (d1.score < d2.score) { + cmp = 1; + } else { + cmp = 0; + } + } else if (sf.getField().equals("sort1")) { cmp = d1.sort1.compareTo(d2.sort1); } else if (sf.getField().equals("sort2")) { cmp = d1.sort2.compareTo(d2.sort2); @@ -213,7 +229,9 @@ public class TestGrouping extends LuceneTestCase { for(int fieldIDX=0;fieldIDX c; final SortField sf = sortFields[fieldIDX]; - if (sf.getField().equals("sort1")) { + if (sf.getType() == SortField.SCORE) { + c = new Float(d.score); + } else if (sf.getField().equals("sort1")) { c = d.sort1; } else if (sf.getField().equals("sort2")) { c = d.sort2; @@ -237,17 +255,17 @@ public class TestGrouping extends LuceneTestCase { */ private TopGroups slowGrouping(GroupDoc[] groupDocs, - String searchTerm, - boolean fillFields, - boolean getScores, - boolean getMaxScores, - boolean doAllGroups, - Sort groupSort, - Sort docSort, - int topNGroups, - int docsPerGroup, - int groupOffset, - int docOffset) { + String searchTerm, + boolean fillFields, + boolean getScores, + boolean getMaxScores, + boolean doAllGroups, + Sort groupSort, + Sort docSort, + int topNGroups, + int docsPerGroup, + int groupOffset, + int docOffset) { final Comparator groupSortComp = getComparator(groupSort); @@ -262,11 +280,11 @@ public class TestGrouping extends LuceneTestCase { //System.out.println("TEST: slowGrouping"); for(GroupDoc d : groupDocs) { // TODO: would be better to filter by searchTerm before sorting! - if (!d.content.equals(searchTerm)) { + if (!d.content.startsWith(searchTerm)) { continue; } totalHitCount++; - //System.out.println(" match id=" + d.id); + //System.out.println(" match id=" + d.id + " score=" + d.score); if (doAllGroups) { if (!knownGroups.contains(d.group)) { @@ -312,9 +330,9 @@ public class TestGrouping extends LuceneTestCase { final GroupDoc d = docs.get(docIDX); final FieldDoc fd; if (fillFields) { - fd = new FieldDoc(d.id, 0.0f, fillFields(d, docSort)); + fd = new FieldDoc(d.id, getScores ? d.score : Float.NaN, fillFields(d, docSort)); } else { - fd = new FieldDoc(d.id, 0.0f); + fd = new FieldDoc(d.id, getScores ? d.score : Float.NaN); } hits[docIDX-docOffset] = fd; } @@ -373,7 +391,7 @@ public class TestGrouping extends LuceneTestCase { doc.add(newField("sort1", groupValue.sort1.utf8ToString(), Field.Index.NOT_ANALYZED)); doc.add(newField("sort2", groupValue.sort2.utf8ToString(), Field.Index.NOT_ANALYZED)); doc.add(new NumericField("id").setIntValue(groupValue.id)); - doc.add(newField("content", groupValue.content, Field.Index.NOT_ANALYZED)); + doc.add(newField("content", groupValue.content, Field.Index.ANALYZED)); //System.out.println("TEST: doc content=" + groupValue.content + " group=" + (groupValue.group == null ? "null" : groupValue.group.utf8ToString()) + " sort1=" + groupValue.sort1.utf8ToString() + " id=" + groupValue.id); } // So we can pull filter marking last doc in block: @@ -421,7 +439,22 @@ public class TestGrouping extends LuceneTestCase { groups.add(new BytesRef(_TestUtil.randomRealisticUnicodeString(random))); //groups.add(new BytesRef(_TestUtil.randomSimpleString(random))); } - final String[] contentStrings = new String[] {"a", "b", "c", "d"}; + final String[] contentStrings = new String[_TestUtil.nextInt(random, 2, 20)]; + if (VERBOSE) { + System.out.println("TEST: create fake content"); + } + for(int contentIDX=0;contentIDX scoreMap = new HashMap(); + + // Tricky: must separately set .score2, because the doc + // block index was created with possible deletions! + for(int contentID=0;contentID<3;contentID++) { + //System.out.println("term=real" + contentID + " dfold=" + s.docFreq(new Term("content", "real"+contentID)) + + //" dfnew=" + s2.docFreq(new Term("content", "real"+contentID))); + final ScoreDoc[] hits = s2.search(new TermQuery(new Term("content", "real"+contentID)), numDocs).scoreDocs; + for(ScoreDoc hit : hits) { + final GroupDoc gd = groupDocsByID[docIDToID2[hit.doc]]; + assertTrue(gd.score2 == 0.0); + gd.score2 = hit.score; + assertEquals(gd.id, docIDToID2[hit.doc]); + //System.out.println(" score=" + hit.score + " id=" + docIDToID2[hit.doc]); + scoreMap.put(gd.score, gd.score2); + } + } + for(int searchIter=0;searchIter<100;searchIter++) { if (VERBOSE) { System.out.println("TEST: searchIter=" + searchIter); } - final String searchTerm = contentStrings[random.nextInt(contentStrings.length)]; + final String searchTerm = "real" + random.nextInt(3); final boolean fillFields = random.nextBoolean(); - final boolean getScores = random.nextBoolean(); + boolean getScores = random.nextBoolean(); final boolean getMaxScores = random.nextBoolean(); final Sort groupSort = getRandomSort(); //final Sort groupSort = new Sort(new SortField[] {new SortField("sort1", SortField.STRING), new SortField("id", SortField.INT)}); // TODO: also test null (= sort by relevance) final Sort docSort = getRandomSort(); + for(SortField sf : docSort.getSort()) { + if (sf.getType() == SortField.SCORE) { + getScores = true; + } + } + + for(SortField sf : groupSort.getSort()) { + if (sf.getType() == SortField.SCORE) { + getScores = true; + } + } + final int topNGroups = _TestUtil.nextInt(random, 1, 30); //final int topNGroups = 4; final int docsPerGroup = _TestUtil.nextInt(random, 1, 50); + final int groupOffset = _TestUtil.nextInt(random, 0, (topNGroups-1)/2); //final int groupOffset = 0; @@ -523,7 +612,7 @@ public class TestGrouping extends LuceneTestCase { final boolean doCache = random.nextBoolean(); final boolean doAllGroups = random.nextBoolean(); if (VERBOSE) { - System.out.println("TEST: groupSort=" + groupSort + " docSort=" + docSort + " searchTerm=" + searchTerm + " topNGroups=" + topNGroups + " groupOffset=" + groupOffset + " docOffset=" + docOffset + " doCache=" + doCache + " docsPerGroup=" + docsPerGroup + " doAllGroups=" + doAllGroups); + System.out.println("TEST: groupSort=" + groupSort + " docSort=" + docSort + " searchTerm=" + searchTerm + " topNGroups=" + topNGroups + " groupOffset=" + groupOffset + " docOffset=" + docOffset + " doCache=" + doCache + " docsPerGroup=" + docsPerGroup + " doAllGroups=" + doAllGroups + " getScores=" + getScores + " getMaxScores=" + getMaxScores); } final TermAllGroupsCollector allGroupsCollector; @@ -636,13 +725,12 @@ public class TestGrouping extends LuceneTestCase { for(GroupDocs gd : expectedGroups.groups) { System.out.println(" group=" + (gd.groupValue == null ? "null" : gd.groupValue.utf8ToString())); for(ScoreDoc sd : gd.scoreDocs) { - System.out.println(" id=" + sd.doc); + System.out.println(" id=" + sd.doc + " score=" + sd.score); } } } } - // NOTE: intentional but temporary field cache insanity! - assertEquals(docIDToID, expectedGroups, groupsResult, true); + assertEquals(docIDToID, expectedGroups, groupsResult, true, getScores); final boolean needsScores = getScores || getMaxScores || docSort == null; final BlockGroupingCollector c3 = new BlockGroupingCollector(groupSort, groupOffset+topNGroups, needsScores, lastDocInBlock); @@ -665,11 +753,53 @@ public class TestGrouping extends LuceneTestCase { } else { groupsResult2 = tempTopGroups2; } - assertEquals(docIDToID2, expectedGroups, groupsResult2, false); + + if (expectedGroups != null) { + // Fixup scores for reader2 + for (GroupDocs groupDocsHits : expectedGroups.groups) { + for(ScoreDoc hit : groupDocsHits.scoreDocs) { + final GroupDoc gd = groupDocsByID[hit.doc]; + assertEquals(gd.id, hit.doc); + //System.out.println("fixup score " + hit.score + " to " + gd.score2 + " vs " + gd.score); + hit.score = gd.score2; + } + } + + final SortField[] sortFields = groupSort.getSort(); + for(int groupSortIDX=0;groupSortIDX \$version, "interval=i" => \$interval, "quiet" => \$quiet); +my $result = GetOptions ("version=s" => \$version, "interval=i" => \$interval); -my $usage = "$0 -v version [ -i interval (seconds; default: 300)] [ -quiet ]"; +my $usage = "$0 -v version [ -i interval (seconds; default: 300) ]"; unless ($result) { print STDERR $usage; @@ -47,26 +48,71 @@ unless (defined($version) && $version =~ /\d+(?:\.\d+)+/) { } my $previously_selected = select STDOUT; -$| = 1; # turn off buffering of STDOUT, so "."s are printed immediately +$| = 1; # turn off buffering of STDOUT, so status is printed immediately select $previously_selected; -my $apache_backup_url = "http://www.apache.org/dist//lucene/java/$version/lucene-$version.tgz.asc"; -my $maven_url = "http://repo2.maven.org/maven2/org/apache/lucene/lucene-core/$version/lucene-core-$version.pom"; +my $apache_url_suffix = "lucene/java/$version/lucene-$version.tgz.asc"; +my $apache_mirrors_list_url = "http://www.apache.org/mirrors/"; +my $maven_url = "http://repo2.maven.org/maven2/org/apache/lucene/lucene-core/$version/lucene-core-$version.pom.asc"; -my $apache_available = 0; my $maven_available = 0; -until ($apache_available && $maven_available) { - unless ($apache_available) { - my $content = get($apache_backup_url); - $apache_available = defined($content); - print "\nDownloadable: $apache_backup_url\n" if ($apache_available); +my @apache_mirrors = (); + +my $apache_mirrors_list_page = get($apache_mirrors_list_url); +if (defined($apache_mirrors_list_page)) { + # + # apache.dattatec.com  @ + # + # http + # 8 hours
    + # 5 hours
    + # ok + # + while ($apache_mirrors_list_page =~ m~(.*?)~gis) { + my $mirror_entry = $1; + next unless ($mirror_entry =~ m~\s*ok\s*\s*$~i); # skip mirrors with problems + if ($mirror_entry =~ m~~i) { + my $mirror_url = $1; + push @apache_mirrors, "$mirror_url/$apache_url_suffix"; + } } +} else { + print STDERR "Error fetching Apache mirrors list $apache_mirrors_list_url"; + exit(1); +} + +my $num_apache_mirrors = $#apache_mirrors; +print "# Apache Mirrors: $num_apache_mirrors\n"; + +while (1) { unless ($maven_available) { my $content = get($maven_url); $maven_available = defined($content); - print "\nDownloadable: $maven_url\n" if ($maven_available); } - print "." unless ($quiet); - sleep($interval) unless ($apache_available && $maven_available); + @apache_mirrors = &check_mirrors; + my $num_downloadable_apache_mirrors + = $num_apache_mirrors - $#apache_mirrors; + + print "Available: "; + print "Maven Central; " if ($maven_available); + printf "%d/%d Apache Mirrors (%0.1f%%)\n", $num_downloadable_apache_mirrors, + $num_apache_mirrors, ($num_downloadable_apache_mirrors*100/$num_apache_mirrors); + last if ($maven_available && $num_downloadable_apache_mirrors == $num_apache_mirrors); + sleep($interval); +} + +sub check_mirrors { + my $agent = LWP::Parallel::UserAgent->new(); + $agent->timeout(30); + $agent->redirect(1); # follow redirects + $agent->register($_) for (@apache_mirrors); + my $entries = $agent->wait(); + my @not_yet_downloadable_apache_mirrors; + for my $entry (keys %$entries) { + my $response = $entries->{$entry}->response; + push @not_yet_downloadable_apache_mirrors, $response->request->uri + unless ($response->is_success); + } + return @not_yet_downloadable_apache_mirrors; } From 1da4ffee6ee7d602b4933f68d7e39f782b39faad Mon Sep 17 00:00:00 2001 From: Yonik Seeley Date: Fri, 3 Jun 2011 20:48:47 +0000 Subject: [PATCH 6/6] SOLR-2136: function queries - add bool type and functions git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1131228 13f79535-47bb-0310-9956-ffa450edef68 --- solr/CHANGES.txt | 4 + .../org/apache/solr/schema/BoolField.java | 91 +++++++- .../apache/solr/search/FunctionQParser.java | 10 +- .../apache/solr/search/MutableValueBool.java | 60 ++++++ .../apache/solr/search/ValueSourceParser.java | 199 ++++++++++++++++++ .../solr/search/function/BoolDocValues.java | 79 +++++++ .../solr/search/function/BoolFunction.java | 23 ++ .../search/function/ConstNumberSource.java | 1 + .../search/function/ConstValueSource.java | 9 + .../solr/search/function/DefFunction.java | 124 +++++++++++ .../solr/search/function/DocValues.java | 4 + .../function/DoubleConstValueSource.java | 5 + .../solr/search/function/DoubleDocValues.java | 5 + .../search/function/DoubleFieldSource.java | 33 +-- .../solr/search/function/IfFunction.java | 148 +++++++++++++ .../solr/search/function/LongDocValues.java | 5 + .../search/function/MultiBoolFunction.java | 105 +++++++++ .../solr/search/function/MultiFunction.java | 122 +++++++++++ .../search/function/SimpleBoolFunction.java | 74 +++++++ .../solr/search/function/StrDocValues.java | 5 + .../search/function/StringIndexDocValues.java | 4 + .../apache/solr/search/TestQueryTypes.java | 24 ++- .../search/function/TestFunctionQuery.java | 52 +++++ 23 files changed, 1149 insertions(+), 37 deletions(-) create mode 100644 solr/src/java/org/apache/solr/search/MutableValueBool.java create mode 100644 solr/src/java/org/apache/solr/search/function/BoolDocValues.java create mode 100644 solr/src/java/org/apache/solr/search/function/BoolFunction.java create mode 100644 solr/src/java/org/apache/solr/search/function/DefFunction.java create mode 100644 solr/src/java/org/apache/solr/search/function/IfFunction.java create mode 100644 solr/src/java/org/apache/solr/search/function/MultiBoolFunction.java create mode 100644 solr/src/java/org/apache/solr/search/function/MultiFunction.java create mode 100644 solr/src/java/org/apache/solr/search/function/SimpleBoolFunction.java diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 17ef07d53fa..a30f7e700ec 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -144,6 +144,10 @@ New Features to IndexReader.open (in the case you have a custom IndexReaderFactory). (simonw via rmuir) +* SOLR-2136: Boolean type added to function queries, along with + new functions exists(), if(), and(), or(), xor(), not(), def(), + and true and false constants. (yonik) + Optimizations ---------------------- diff --git a/solr/src/java/org/apache/solr/schema/BoolField.java b/solr/src/java/org/apache/solr/schema/BoolField.java index 0332b829d1c..3cd50247abc 100644 --- a/solr/src/java/org/apache/solr/schema/BoolField.java +++ b/solr/src/java/org/apache/solr/schema/BoolField.java @@ -17,12 +17,16 @@ package org.apache.solr.schema; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.FieldCache; import org.apache.lucene.search.SortField; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.CharsRef; +import org.apache.solr.search.MutableValue; +import org.apache.solr.search.MutableValueBool; +import org.apache.solr.search.MutableValueInt; import org.apache.solr.search.QParser; -import org.apache.solr.search.function.ValueSource; -import org.apache.solr.search.function.OrdFieldSource; +import org.apache.solr.search.function.*; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; @@ -50,7 +54,7 @@ public class BoolField extends FieldType { @Override public ValueSource getValueSource(SchemaField field, QParser qparser) { field.checkFieldCacheSource(qparser); - return new OrdFieldSource(field.name); + return new BoolFieldSource(field.name); } // avoid instantiating every time... @@ -121,7 +125,7 @@ public class BoolField extends FieldType { @Override public Object toObject(SchemaField sf, BytesRef term) { - return term.bytes[0] == 'T'; + return term.bytes[term.offset] == 'T'; } @Override @@ -145,6 +149,83 @@ public class BoolField extends FieldType { @Override public void write(TextResponseWriter writer, String name, Fieldable f) throws IOException { - writer.writeBool(name, f.stringValue().charAt(0) =='T'); + writer.writeBool(name, f.stringValue().charAt(0) == 'T'); } } + +// TODO - this can be much more efficient - use OpenBitSet or Bits +class BoolFieldSource extends ValueSource { + protected String field; + + public BoolFieldSource(String field) { + this.field = field; + } + + @Override + public String description() { + return "bool(" + field + ')'; + } + + + @Override + public DocValues getValues(Map context, IndexReader.AtomicReaderContext readerContext) throws IOException { + final FieldCache.DocTermsIndex sindex = FieldCache.DEFAULT.getTermsIndex(readerContext.reader, field); + + // figure out what ord maps to true + int nord = sindex.numOrd(); + BytesRef br = new BytesRef(); + int tord = -1; + for (int i=1; i sources = fp.parseValueSourceList(); + return new MultiBoolFunction(sources) { + @Override + protected String name() { + return "and"; + } + @Override + protected boolean func(int doc, DocValues[] vals) { + for (DocValues dv : vals) + if (!dv.boolVal(doc)) return false; + return true; + } + }; + } + }); + + addParser("or", new ValueSourceParser() { + @Override + public ValueSource parse(FunctionQParser fp) throws ParseException { + List sources = fp.parseValueSourceList(); + return new MultiBoolFunction(sources) { + @Override + protected String name() { + return "or"; + } + @Override + protected boolean func(int doc, DocValues[] vals) { + for (DocValues dv : vals) + if (dv.boolVal(doc)) return true; + return false; + } + }; + } + }); + + addParser("xor", new ValueSourceParser() { + @Override + public ValueSource parse(FunctionQParser fp) throws ParseException { + List sources = fp.parseValueSourceList(); + return new MultiBoolFunction(sources) { + @Override + protected String name() { + return "xor"; + } + @Override + protected boolean func(int doc, DocValues[] vals) { + int nTrue=0, nFalse=0; + for (DocValues dv : vals) { + if (dv.boolVal(doc)) nTrue++; + else nFalse++; + } + return nTrue != 0 && nFalse != 0; + } + }; + } + }); + + addParser("if", new ValueSourceParser() { + @Override + public ValueSource parse(FunctionQParser fp) throws ParseException { + ValueSource ifValueSource = fp.parseValueSource(); + ValueSource trueValueSource = fp.parseValueSource(); + ValueSource falseValueSource = fp.parseValueSource(); + + return new IfFunction(ifValueSource, trueValueSource, falseValueSource); + } + }); + + addParser("def", new ValueSourceParser() { + @Override + public ValueSource parse(FunctionQParser fp) throws ParseException { + return new DefFunction(fp.parseValueSourceList()); + } + }); + } private static TInfo parseTerm(FunctionQParser fp) throws ParseException { @@ -857,6 +985,11 @@ class LongConstValueSource extends ConstNumberSource { public Number getNumber() { return constant; } + + @Override + public boolean getBool() { + return constant != 0; + } } @@ -981,3 +1114,69 @@ abstract class Double2Parser extends NamedParser { } } + + +class BoolConstValueSource extends ConstNumberSource { + final boolean constant; + + public BoolConstValueSource(boolean constant) { + this.constant = constant; + } + + @Override + public String description() { + return "const(" + constant + ")"; + } + + @Override + public DocValues getValues(Map context, AtomicReaderContext readerContext) throws IOException { + return new BoolDocValues(this) { + @Override + public boolean boolVal(int doc) { + return constant; + } + }; + } + + @Override + public int hashCode() { + return constant ? 0x12345678 : 0x87654321; + } + + @Override + public boolean equals(Object o) { + if (BoolConstValueSource.class != o.getClass()) return false; + BoolConstValueSource other = (BoolConstValueSource) o; + return this.constant == other.constant; + } + + @Override + public int getInt() { + return constant ? 1 : 0; + } + + @Override + public long getLong() { + return constant ? 1 : 0; + } + + @Override + public float getFloat() { + return constant ? 1 : 0; + } + + @Override + public double getDouble() { + return constant ? 1 : 0; + } + + @Override + public Number getNumber() { + return constant ? 1 : 0; + } + + @Override + public boolean getBool() { + return constant; + } +} diff --git a/solr/src/java/org/apache/solr/search/function/BoolDocValues.java b/solr/src/java/org/apache/solr/search/function/BoolDocValues.java new file mode 100644 index 00000000000..443f379ab95 --- /dev/null +++ b/solr/src/java/org/apache/solr/search/function/BoolDocValues.java @@ -0,0 +1,79 @@ +package org.apache.solr.search.function; + +import org.apache.solr.search.MutableValue; +import org.apache.solr.search.MutableValueBool; +import org.apache.solr.search.MutableValueInt; + +public abstract class BoolDocValues extends DocValues { + protected final ValueSource vs; + + public BoolDocValues(ValueSource vs) { + this.vs = vs; + } + + @Override + public abstract boolean boolVal(int doc); + + @Override + public byte byteVal(int doc) { + return boolVal(doc) ? (byte)1 : (byte)0; + } + + @Override + public short shortVal(int doc) { + return boolVal(doc) ? (short)1 : (short)0; + } + + @Override + public float floatVal(int doc) { + return boolVal(doc) ? (float)1 : (float)0; + } + + @Override + public int intVal(int doc) { + return boolVal(doc) ? 1 : 0; + } + + @Override + public long longVal(int doc) { + return boolVal(doc) ? (long)1 : (long)0; + } + + @Override + public double doubleVal(int doc) { + return boolVal(doc) ? (double)1 : (double)0; + } + + @Override + public String strVal(int doc) { + return Boolean.toString(boolVal(doc)); + } + + @Override + public Object objectVal(int doc) { + return exists(doc) ? boolVal(doc) : null; + } + + @Override + public String toString(int doc) { + return vs.description() + '=' + strVal(doc); + } + + @Override + public ValueFiller getValueFiller() { + return new ValueFiller() { + private final MutableValueBool mval = new MutableValueBool(); + + @Override + public MutableValue getValue() { + return mval; + } + + @Override + public void fillValue(int doc) { + mval.value = boolVal(doc); + mval.exists = exists(doc); + } + }; + } +} diff --git a/solr/src/java/org/apache/solr/search/function/BoolFunction.java b/solr/src/java/org/apache/solr/search/function/BoolFunction.java new file mode 100644 index 00000000000..b7898d15184 --- /dev/null +++ b/solr/src/java/org/apache/solr/search/function/BoolFunction.java @@ -0,0 +1,23 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.search.function; + + +public abstract class BoolFunction extends ValueSource { + // TODO: placeholder to return type, among other common future functionality +} diff --git a/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java b/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java index da9ef1f051a..fac0611cc71 100755 --- a/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java +++ b/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java @@ -26,4 +26,5 @@ public abstract class ConstNumberSource extends ValueSource { public abstract float getFloat(); public abstract double getDouble(); public abstract Number getNumber(); + public abstract boolean getBool(); } diff --git a/solr/src/java/org/apache/solr/search/function/ConstValueSource.java b/solr/src/java/org/apache/solr/search/function/ConstValueSource.java index ad495a18007..fc0b9334427 100755 --- a/solr/src/java/org/apache/solr/search/function/ConstValueSource.java +++ b/solr/src/java/org/apache/solr/search/function/ConstValueSource.java @@ -66,6 +66,10 @@ public class ConstValueSource extends ConstNumberSource { public Object objectVal(int doc) { return constant; } + @Override + public boolean boolVal(int doc) { + return constant != 0.0f; + } }; } @@ -105,4 +109,9 @@ public class ConstValueSource extends ConstNumberSource { public Number getNumber() { return constant; } + + @Override + public boolean getBool() { + return constant != 0.0f; + } } diff --git a/solr/src/java/org/apache/solr/search/function/DefFunction.java b/solr/src/java/org/apache/solr/search/function/DefFunction.java new file mode 100644 index 00000000000..b2f99a3c6fa --- /dev/null +++ b/solr/src/java/org/apache/solr/search/function/DefFunction.java @@ -0,0 +1,124 @@ +package org.apache.solr.search.function; +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.apache.lucene.index.IndexReader.AtomicReaderContext; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.util.BytesRef; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class DefFunction extends MultiFunction { + public DefFunction(List sources) { + super(sources); + } + + @Override + protected String name() { + return "def"; + } + + + @Override + public DocValues getValues(Map fcontext, AtomicReaderContext readerContext) throws IOException { + + + return new Values(valsArr(sources, fcontext, readerContext)) { + final int upto = valsArr.length - 1; + + private DocValues get(int doc) { + for (int i=0; i { final double[] arr = vals.values; final Bits valid = vals.valid; - return new DocValues() { - @Override - public float floatVal(int doc) { - return (float) arr[doc]; - } - - @Override - public int intVal(int doc) { - return (int) arr[doc]; - } - - @Override - public long longVal(int doc) { - return (long) arr[doc]; - } - + return new DoubleDocValues(this) { @Override public double doubleVal(int doc) { return arr[doc]; } @Override - public String strVal(int doc) { - return Double.toString(arr[doc]); - } - - @Override - public Object objectVal(int doc) { - return valid.get(doc) ? arr[doc] : null; - } - - @Override - public String toString(int doc) { - return description() + '=' + doubleVal(doc); + public boolean exists(int doc) { + return valid.get(doc); } @Override @@ -147,7 +122,7 @@ public class DoubleFieldSource extends NumericFieldCacheSource { } } - @Override + @Override public ValueFiller getValueFiller() { return new ValueFiller() { private final double[] doubleArr = arr; diff --git a/solr/src/java/org/apache/solr/search/function/IfFunction.java b/solr/src/java/org/apache/solr/search/function/IfFunction.java new file mode 100644 index 00000000000..00ad2f437d6 --- /dev/null +++ b/solr/src/java/org/apache/solr/search/function/IfFunction.java @@ -0,0 +1,148 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.search.function; + +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexReader.AtomicReaderContext; +import org.apache.lucene.search.Explanation; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.util.BytesRef; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + + +public class IfFunction extends BoolFunction { + private ValueSource ifSource; + private ValueSource trueSource; + private ValueSource falseSource; + + + public IfFunction(ValueSource ifSource, ValueSource trueSource, ValueSource falseSource) { + this.ifSource = ifSource; + this.trueSource = trueSource; + this.falseSource = falseSource; + } + + @Override + public DocValues getValues(Map context, AtomicReaderContext readerContext) throws IOException { + final DocValues ifVals = ifSource.getValues(context, readerContext); + final DocValues trueVals = trueSource.getValues(context, readerContext); + final DocValues falseVals = falseSource.getValues(context, readerContext); + + return new DocValues() { + @Override + public byte byteVal(int doc) { + return ifVals.boolVal(doc) ? trueVals.byteVal(doc) : falseVals.byteVal(doc); + } + + @Override + public short shortVal(int doc) { + return ifVals.boolVal(doc) ? trueVals.shortVal(doc) : falseVals.shortVal(doc); + } + + @Override + public float floatVal(int doc) { + return ifVals.boolVal(doc) ? trueVals.floatVal(doc) : falseVals.floatVal(doc); + } + + @Override + public int intVal(int doc) { + return ifVals.boolVal(doc) ? trueVals.intVal(doc) : falseVals.intVal(doc); + } + + @Override + public long longVal(int doc) { + return ifVals.boolVal(doc) ? trueVals.longVal(doc) : falseVals.longVal(doc); + } + + @Override + public double doubleVal(int doc) { + return ifVals.boolVal(doc) ? trueVals.doubleVal(doc) : falseVals.doubleVal(doc); + } + + @Override + public String strVal(int doc) { + return ifVals.boolVal(doc) ? trueVals.strVal(doc) : falseVals.strVal(doc); + } + + @Override + public boolean boolVal(int doc) { + return ifVals.boolVal(doc) ? trueVals.boolVal(doc) : falseVals.boolVal(doc); + } + + @Override + public boolean bytesVal(int doc, BytesRef target) { + return ifVals.boolVal(doc) ? trueVals.bytesVal(doc, target) : falseVals.bytesVal(doc, target); + } + + @Override + public Object objectVal(int doc) { + return ifVals.boolVal(doc) ? trueVals.objectVal(doc) : falseVals.objectVal(doc); + } + + @Override + public boolean exists(int doc) { + return true; // TODO: flow through to any sub-sources? + } + + @Override + public ValueFiller getValueFiller() { + // TODO: we need types of trueSource / falseSource to handle this + // for now, use float. + return super.getValueFiller(); + } + + @Override + public String toString(int doc) { + return "if(" + ifVals.toString(doc) + ',' + trueVals.toString(doc) + ',' + falseVals.toString(doc) + ')'; + } + }; + + } + + @Override + public String description() { + return "if(" + ifSource.description() + ',' + trueSource.description() + ',' + falseSource + ')'; + } + + @Override + public int hashCode() { + int h = ifSource.hashCode(); + h = h * 31 + trueSource.hashCode(); + h = h * 31 + falseSource.hashCode(); + return h; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof IfFunction)) return false; + IfFunction other = (IfFunction)o; + return ifSource.equals(other.ifSource) + && trueSource.equals(other.trueSource) + && falseSource.equals(other.falseSource); + } + + @Override + public void createWeight(Map context, IndexSearcher searcher) throws IOException { + ifSource.createWeight(context, searcher); + trueSource.createWeight(context, searcher); + falseSource.createWeight(context, searcher); + } +} \ No newline at end of file diff --git a/solr/src/java/org/apache/solr/search/function/LongDocValues.java b/solr/src/java/org/apache/solr/search/function/LongDocValues.java index f5117bd0d43..f0e8f6d8ee9 100644 --- a/solr/src/java/org/apache/solr/search/function/LongDocValues.java +++ b/solr/src/java/org/apache/solr/search/function/LongDocValues.java @@ -38,6 +38,11 @@ public abstract class LongDocValues extends DocValues { return (double)longVal(doc); } + @Override + public boolean boolVal(int doc) { + return longVal(doc) != 0; + } + @Override public String strVal(int doc) { return Long.toString(longVal(doc)); diff --git a/solr/src/java/org/apache/solr/search/function/MultiBoolFunction.java b/solr/src/java/org/apache/solr/search/function/MultiBoolFunction.java new file mode 100644 index 00000000000..033ef6ebae9 --- /dev/null +++ b/solr/src/java/org/apache/solr/search/function/MultiBoolFunction.java @@ -0,0 +1,105 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.search.function; + +import org.apache.lucene.index.IndexReader.AtomicReaderContext; +import org.apache.lucene.search.IndexSearcher; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + + +public abstract class MultiBoolFunction extends BoolFunction { + protected final List sources; + + public MultiBoolFunction(List sources) { + this.sources = sources; + } + + protected abstract String name(); + + protected abstract boolean func(int doc, DocValues[] vals); + + @Override + public BoolDocValues getValues(Map context, AtomicReaderContext readerContext) throws IOException { + final DocValues[] vals = new DocValues[sources.size()]; + int i=0; + for (ValueSource source : sources) { + vals[i++] = source.getValues(context, readerContext); + } + + return new BoolDocValues(this) { + @Override + public boolean boolVal(int doc) { + return func(doc, vals); + } + + @Override + public String toString(int doc) { + StringBuilder sb = new StringBuilder(name()); + sb.append('('); + boolean first = true; + for (DocValues dv : vals) { + if (first) { + first = false; + } else { + sb.append(','); + } + sb.append(dv.toString(doc)); + } + return sb.toString(); + } + }; + } + + @Override + public String description() { + StringBuilder sb = new StringBuilder(name()); + sb.append('('); + boolean first = true; + for (ValueSource source : sources) { + if (first) { + first = false; + } else { + sb.append(','); + } + sb.append(source.description()); + } + return sb.toString(); + } + + @Override + public int hashCode() { + return sources.hashCode() + name().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this.getClass() != o.getClass()) return false; + MultiBoolFunction other = (MultiBoolFunction)o; + return this.sources.equals(other.sources); + } + + @Override + public void createWeight(Map context, IndexSearcher searcher) throws IOException { + for (ValueSource source : sources) { + source.createWeight(context, searcher); + } + } +} \ No newline at end of file diff --git a/solr/src/java/org/apache/solr/search/function/MultiFunction.java b/solr/src/java/org/apache/solr/search/function/MultiFunction.java new file mode 100644 index 00000000000..941b3415ba7 --- /dev/null +++ b/solr/src/java/org/apache/solr/search/function/MultiFunction.java @@ -0,0 +1,122 @@ +package org.apache.solr.search.function; +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.apache.lucene.index.IndexReader.AtomicReaderContext; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.util.BytesRef; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + + +public abstract class MultiFunction extends ValueSource { + protected final List sources; + + public MultiFunction(List sources) { + this.sources = sources; + } + + abstract protected String name(); + + @Override + public String description() { + return description(name(), sources); + } + + public static String description(String name, List sources) { + StringBuilder sb = new StringBuilder(); + sb.append(name).append('('); + boolean firstTime=true; + for (ValueSource source : sources) { + if (firstTime) { + firstTime=false; + } else { + sb.append(','); + } + sb.append(source); + } + sb.append(')'); + return sb.toString(); + } + + public static DocValues[] valsArr(List sources, Map fcontext, AtomicReaderContext readerContext) throws IOException { + final DocValues[] valsArr = new DocValues[sources.size()]; + int i=0; + for (ValueSource source : sources) { + valsArr[i++] = source.getValues(fcontext, readerContext); + } + return valsArr; + } + + public class Values extends DocValues { + final DocValues[] valsArr; + + public Values(DocValues[] valsArr) { + this.valsArr = valsArr; + } + + @Override + public String toString(int doc) { + return MultiFunction.toString(name(), valsArr, doc); + } + + @Override + public ValueFiller getValueFiller() { + // TODO: need ValueSource.type() to determine correct type + return super.getValueFiller(); + } + } + + + public static String toString(String name, DocValues[] valsArr, int doc) { + StringBuilder sb = new StringBuilder(); + sb.append(name).append('('); + boolean firstTime=true; + for (DocValues vals : valsArr) { + if (firstTime) { + firstTime=false; + } else { + sb.append(','); + } + sb.append(vals.toString(doc)); + } + sb.append(')'); + return sb.toString(); + } + + @Override + public void createWeight(Map context, IndexSearcher searcher) throws IOException { + for (ValueSource source : sources) + source.createWeight(context, searcher); + } + + @Override + public int hashCode() { + return sources.hashCode() + name().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this.getClass() != o.getClass()) return false; + MultiFunction other = (MultiFunction)o; + return this.sources.equals(other.sources); + } +} + diff --git a/solr/src/java/org/apache/solr/search/function/SimpleBoolFunction.java b/solr/src/java/org/apache/solr/search/function/SimpleBoolFunction.java new file mode 100644 index 00000000000..6a4da8b229d --- /dev/null +++ b/solr/src/java/org/apache/solr/search/function/SimpleBoolFunction.java @@ -0,0 +1,74 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.search.function; + +import org.apache.lucene.index.IndexReader.AtomicReaderContext; +import org.apache.lucene.search.IndexSearcher; + +import java.io.IOException; +import java.util.Map; + + +public abstract class SimpleBoolFunction extends BoolFunction { + protected final ValueSource source; + + public SimpleBoolFunction(ValueSource source) { + this.source = source; + } + + protected abstract String name(); + + protected abstract boolean func(int doc, DocValues vals); + + @Override + public BoolDocValues getValues(Map context, AtomicReaderContext readerContext) throws IOException { + final DocValues vals = source.getValues(context, readerContext); + return new BoolDocValues(this) { + @Override + public boolean boolVal(int doc) { + return func(doc, vals); + } + @Override + public String toString(int doc) { + return name() + '(' + vals.toString(doc) + ')'; + } + }; + } + + @Override + public String description() { + return name() + '(' + source.description() + ')'; + } + + @Override + public int hashCode() { + return source.hashCode() + name().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this.getClass() != o.getClass()) return false; + SingleFunction other = (SingleFunction)o; + return this.source.equals(other.source); + } + + @Override + public void createWeight(Map context, IndexSearcher searcher) throws IOException { + source.createWeight(context, searcher); + } +} \ No newline at end of file diff --git a/solr/src/java/org/apache/solr/search/function/StrDocValues.java b/solr/src/java/org/apache/solr/search/function/StrDocValues.java index e4c28da47eb..5726824388c 100644 --- a/solr/src/java/org/apache/solr/search/function/StrDocValues.java +++ b/solr/src/java/org/apache/solr/search/function/StrDocValues.java @@ -21,6 +21,11 @@ public abstract class StrDocValues extends DocValues { return exists(doc) ? strVal(doc) : null; } + @Override + public boolean boolVal(int doc) { + return exists(doc); + } + @Override public String toString(int doc) { return vs.description() + "='" + strVal(doc) + "'"; diff --git a/solr/src/java/org/apache/solr/search/function/StringIndexDocValues.java b/solr/src/java/org/apache/solr/search/function/StringIndexDocValues.java index 95d7d0cd823..71db0ab36d0 100755 --- a/solr/src/java/org/apache/solr/search/function/StringIndexDocValues.java +++ b/solr/src/java/org/apache/solr/search/function/StringIndexDocValues.java @@ -78,6 +78,10 @@ public abstract class StringIndexDocValues extends DocValues { return spareChars.toString(); } + @Override + public boolean boolVal(int doc) { + return exists(doc); + } @Override public abstract Object objectVal(int doc); // force subclasses to override diff --git a/solr/src/test/org/apache/solr/search/TestQueryTypes.java b/solr/src/test/org/apache/solr/search/TestQueryTypes.java index ca49dd72b3f..efd6c68f547 100755 --- a/solr/src/test/org/apache/solr/search/TestQueryTypes.java +++ b/solr/src/test/org/apache/solr/search/TestQueryTypes.java @@ -119,7 +119,29 @@ public class TestQueryTypes extends AbstractSolrTestCase { assertQ(req( "q", "{!frange v="+f+" l='"+v+"' u='"+v+"'}" ) ,"//result[@numFound='1']" ); - + + // exists() + assertQ(req( "fq","id:999", "q", "{!frange l=1 u=1}if(exists("+f+"),1,0)" ) + ,"//result[@numFound='1']" + ); + + // boolean value of non-zero values (just leave off the exists from the prev test) + assertQ(req( "fq","id:999", "q", "{!frange l=1 u=1}if("+f+",1,0)" ) + ,"//result[@numFound='1']" + ); + + if (!"id".equals(f)) { + assertQ(req( "fq","id:1", "q", "{!frange l=1 u=1}if(exists("+f+"),1,0)" ) + ,"//result[@numFound='0']" + ); + + // boolean value of zero/missing values (just leave off the exists from the prev test) + assertQ(req( "fq","id:1", "q", "{!frange l=1 u=1}if("+f+",1,0)" ) + ,"//result[@numFound='0']" + ); + + } + // function query... just make sure it doesn't throw an exception if ("v_s".equals(f)) continue; // in this context, functions must be able to be interpreted as a float assertQ(req( "q", "+id:999 _val_:\"" + f + "\"") diff --git a/solr/src/test/org/apache/solr/search/function/TestFunctionQuery.java b/solr/src/test/org/apache/solr/search/function/TestFunctionQuery.java index 1bf6dd8edfc..4648b424126 100755 --- a/solr/src/test/org/apache/solr/search/function/TestFunctionQuery.java +++ b/solr/src/test/org/apache/solr/search/function/TestFunctionQuery.java @@ -581,4 +581,56 @@ public class TestFunctionQuery extends SolrTestCaseJ4 { purgeFieldCache(FieldCache.DEFAULT); // avoid FC insanity } + @Test + public void testBooleanFunctions() throws Exception { + assertU(adoc("id", "1", "text", "hello", "foo_s","A", "foo_ti", "0", "foo_tl","0")); + assertU(adoc("id", "2" , "foo_ti","10", "foo_tl","11")); + assertU(commit()); + + // true and false functions and constants + assertJQ(req("q", "id:1", "fl", "t:true(),f:false(),tt:{!func}true,ff:{!func}false") + , "/response/docs/[0]=={'t':true,'f':false,'tt':true,'ff':false}"); + + // test that exists(query) depends on the query matching the document + assertJQ(req("q", "id:1", "fl", "t:exists(query($q1)),f:exists(query($q2))", "q1","text:hello", "q2","text:there") + , "/response/docs/[0]=={'t':true,'f':false}"); + + // test if() + assertJQ(req("q", "id:1", "fl", "a1:if(true,'A','B')", "fl","b1:if(false,'A','B')") + , "/response/docs/[0]=={'a1':'A', 'b1':'B'}"); + + // test boolean operators + assertJQ(req("q", "id:1", "fl", "t1:and(true,true)", "fl","f1:and(true,false)", "fl","f2:and(false,true)", "fl","f3:and(false,false)") + , "/response/docs/[0]=={'t1':true, 'f1':false, 'f2':false, 'f3':false}"); + assertJQ(req("q", "id:1", "fl", "t1:or(true,true)", "fl","t2:or(true,false)", "fl","t3:or(false,true)", "fl","f1:or(false,false)") + , "/response/docs/[0]=={'t1':true, 't2':true, 't3':true, 'f1':false}"); + assertJQ(req("q", "id:1", "fl", "f1:xor(true,true)", "fl","t1:xor(true,false)", "fl","t2:xor(false,true)", "fl","f2:xor(false,false)") + , "/response/docs/[0]=={'t1':true, 't2':true, 'f1':false, 'f2':false}"); + assertJQ(req("q", "id:1", "fl", "t:not(false),f:not(true)") + , "/response/docs/[0]=={'t':true, 'f':false}"); + + + // def(), the default function that returns the first value that exists + assertJQ(req("q", "id:1", "fl", "x:def(id,123.0), y:def(foo_f,234.0)") + , "/response/docs/[0]=={'x':1.0, 'y':234.0}"); + assertJQ(req("q", "id:1", "fl", "x:def(foo_s,'Q'), y:def(missing_s,'W')") + , "/response/docs/[0]=={'x':'A', 'y':'W'}"); + + // test constant conversion to boolean + assertJQ(req("q", "id:1", "fl", "a:not(0), b:not(1), c:not(0.0), d:not(1.1), e:not('A')") + , "/response/docs/[0]=={'a':true, 'b':false, 'c':true, 'd':false, 'e':false}"); + + } + + + @Test + public void testPseudoFieldFunctions() throws Exception { + assertU(adoc("id", "1", "text", "hello", "foo_s","A")); + assertU(adoc("id", "2")); + assertU(commit()); + + assertJQ(req("q", "id:1", "fl", "a:1,b:2.0,c:'X',d:{!func}foo_s,e:{!func}bar_s") // if exists() is false, no pseudo-field should be added + , "/response/docs/[0]=={'a':1, 'b':2.0,'c':'X','d':'A'}"); + } + }