Split files.
Showing
7 changed files
with
573 additions
and
547 deletions
... | @@ -11,7 +11,7 @@ module.exports = function(grunt) { | ... | @@ -11,7 +11,7 @@ module.exports = function(grunt) { |
11 | browserOptions: { | 11 | browserOptions: { |
12 | }, | 12 | }, |
13 | model: [ | 13 | model: [ |
14 | 'src/scripts/solr-search/model/**/*.js', | 14 | 'src/scripts/solr/model/**/*.js', |
15 | ], | 15 | ], |
16 | }; | 16 | }; |
17 | /* | 17 | /* | ... | ... |
... | @@ -3,49 +3,15 @@ define(function(require) { | ... | @@ -3,49 +3,15 @@ define(function(require) { |
3 | var _ = require('underscore'); | 3 | var _ = require('underscore'); |
4 | var Backbone = require('backbone'); | 4 | var Backbone = require('backbone'); |
5 | var NestedModels = require('backbone-nested-models'); | 5 | var NestedModels = require('backbone-nested-models'); |
6 | //var module = require('module'); | ||
7 | 6 | ||
8 | function getField(obj, key) { | 7 | function getField(obj, key) { |
9 | return obj[key]; | 8 | return obj[key]; |
10 | } | 9 | } |
11 | 10 | ||
12 | function mergeStaticProps(startPtr, endPtr, obj, fieldName) { | ||
13 | var result = obj; | ||
14 | var ptr = startPtr; | ||
15 | while (true) { | ||
16 | result = _.extend(result, _.result(ptr, fieldName)); | ||
17 | if (ptr === endPtr) { | ||
18 | break; | ||
19 | } | ||
20 | ptr = ptr.__super__.constructor; | ||
21 | } | ||
22 | return result; | ||
23 | } | ||
24 | |||
25 | function mergeStaticSets(startPtr, endPtr, obj, fieldName) { | ||
26 | function arrayToMap(array) { | ||
27 | var result = {}; | ||
28 | _.each(array, function(value) { | ||
29 | result[value] = true; | ||
30 | }); | ||
31 | return result; | ||
32 | } | ||
33 | var set = arrayToMap(obj); | ||
34 | var ptr = startPtr; | ||
35 | while (true) { | ||
36 | set = _.extend(set, arrayToMap(_.result(ptr, fieldName))); | ||
37 | if (ptr === endPtr) { | ||
38 | break; | ||
39 | } | ||
40 | ptr = ptr.__super__.constructor; | ||
41 | } | ||
42 | return _.keys(set); | ||
43 | } | ||
44 | |||
45 | function getItemKeyAccessor(item) { | 11 | function getItemKeyAccessor(item) { |
46 | return item.get('key'); | 12 | return item.get('key'); |
47 | } | 13 | } |
48 | var SolrFacets = (function() { | 14 | |
49 | var Facets = NestedModels.mixin(Backbone.Model.extend({ | 15 | var Facets = NestedModels.mixin(Backbone.Model.extend({ |
50 | initialize: function(data, options) { | 16 | initialize: function(data, options) { |
51 | this.url = function() { | 17 | this.url = function() { |
... | @@ -621,515 +587,4 @@ define(function(require) { | ... | @@ -621,515 +587,4 @@ define(function(require) { |
621 | }); | 587 | }); |
622 | 588 | ||
623 | return Facets; | 589 | return Facets; |
624 | })(); | ||
625 | var Pagination = (function() { | ||
626 | function stepFalse(e) { | ||
627 | e.preventDefault(); | ||
628 | return false; | ||
629 | } | ||
630 | var Pagination = Backbone.Model.extend({ | ||
631 | defaults: { | ||
632 | currentPage: 1, | ||
633 | hasNext: false, | ||
634 | hasNextJumpPage: false, | ||
635 | hasPrevious: false, | ||
636 | hasPreviousJumpPage: false, | ||
637 | nextJumpPage: undefined, | ||
638 | nextPage: undefined, | ||
639 | pages: [], | ||
640 | pageJump: 5, | ||
641 | pageSize: 10, | ||
642 | previousJumpPage: undefined, | ||
643 | previousPage: undefined, | ||
644 | totalCount: 0, | ||
645 | totalPages: 0, | ||
646 | }, | ||
647 | initialize: function(data, options) { | ||
648 | var buildPages = function buildPages() { | ||
649 | var currentPage = parseInt(this.get('currentPage')); | ||
650 | var pageSize = parseInt(this.get('pageSize')); | ||
651 | var totalCount = parseInt(this.get('totalCount')); | ||
652 | if (!currentPage || !pageSize || !totalCount) { | ||
653 | return; | ||
654 | } | ||
655 | var pages = []; | ||
656 | var totalPages = Math.floor((totalCount + pageSize - 1) / pageSize); | ||
657 | function addPage(self, i) { | ||
658 | pages.push({ | ||
659 | current: i === currentPage, | ||
660 | jump: function() { | ||
661 | self.set('currentPage', i); | ||
662 | return true; | ||
663 | }, | ||
664 | number: i, | ||
665 | }); | ||
666 | } | ||
667 | var startAt = currentPage - 4, endAt = currentPage + 5; | ||
668 | if (startAt < 1) { | ||
669 | endAt += (1 - startAt); | ||
670 | } | ||
671 | if (endAt > totalPages) { | ||
672 | startAt -= endAt - totalPages - 1; | ||
673 | endAt = totalPages + 1; | ||
674 | } | ||
675 | if (startAt < 1) { | ||
676 | startAt = 1; | ||
677 | } | ||
678 | if (endAt - startAt < 9) { | ||
679 | /* global console:false */ | ||
680 | console.log('foo'); | ||
681 | } | ||
682 | |||
683 | for (var i = startAt; i < endAt; i++) { | ||
684 | if (i > 0 && i <= totalPages) { | ||
685 | addPage(this, i); | ||
686 | } | ||
687 | } | ||
688 | var hasPrevious = currentPage > 1; | ||
689 | var hasNext = currentPage < totalPages; | ||
690 | var pageJump = this.get('pageJump'); | ||
691 | var nextJumpPage, previousJumpPage, hasNextJump, hasPreviousJump; | ||
692 | if (pageJump) { | ||
693 | nextJumpPage = currentPage + pageJump; | ||
694 | previousJumpPage = currentPage - pageJump; | ||
695 | hasNextJump = nextJumpPage < totalPages; | ||
696 | hasPreviousJump = previousJumpPage > 0; | ||
697 | } else { | ||
698 | hasNextJump = false; | ||
699 | hasPreviousJump = false; | ||
700 | } | ||
701 | this.set({ | ||
702 | hasNext: hasNext, | ||
703 | hasNextJump: hasNextJump, | ||
704 | hasPrevious: hasPrevious, | ||
705 | hasPreviousJump: hasPreviousJump, | ||
706 | nextJumpPage: hasNextJump ? nextJumpPage : undefined, | ||
707 | nextPage: hasNext ? currentPage + 1 : undefined, | ||
708 | pages: pages, | ||
709 | previousJumpPage: hasNextJump ? previousJumpPage : undefined, | ||
710 | previousPage: hasPrevious ? currentPage - 1 : undefined, | ||
711 | totalPages: totalPages, | ||
712 | }); | ||
713 | }; | ||
714 | this.on('change:pageSize change:currentPage change:totalCount', buildPages, this); | ||
715 | var installActions = _.bind(function installActions(eventName, nextName, hasNextName, previousName, hasPreviousName, pageCount) { | ||
716 | this.on(eventName, function() { | ||
717 | var hasNext = this.get(hasNextName); | ||
718 | var hasPrevious = this.get(hasPreviousName); | ||
719 | var next, previous; | ||
720 | if (hasNext) { | ||
721 | next = _.bind(function(e) { | ||
722 | e.preventDefault(); | ||
723 | this.set('currentPage', this.get('currentPage') + pageCount); | ||
724 | return false; | ||
725 | }, this); | ||
726 | } else { | ||
727 | next = stepFalse; | ||
728 | } | ||
729 | if (hasPrevious) { | ||
730 | previous = _.bind(function(e) { | ||
731 | e.preventDefault(); | ||
732 | this.set('currentPage', this.get('currentPage') - pageCount); | ||
733 | return false; | ||
734 | }, this); | ||
735 | } else { | ||
736 | previous = stepFalse; | ||
737 | } | ||
738 | this.set(nextName, next); | ||
739 | this.set(previousName, previous); | ||
740 | }, this); | ||
741 | this[nextName] = _.bind(function() { | ||
742 | return this.get(nextName)(); | ||
743 | }, this); | ||
744 | this[previousName] = _.bind(function() { | ||
745 | return this.get(previousName)(); | ||
746 | }, this); | ||
747 | }, this); | ||
748 | this.on('change:pageJump', function() { | ||
749 | var pageJump = this.get('pageJump'); | ||
750 | installActions('change:hasNextJump change:hasPreviousJump', 'nextJump', 'hasNextJump', 'previousJump', 'hasPreviousJump', pageJump); | ||
751 | }, this); | ||
752 | installActions('change:hasNext change:hasPrevious', 'next', 'hasNext', 'previous', 'hasPrevious', 1); | ||
753 | buildPages.apply(this); | ||
754 | this.trigger('change:pageJump'); | ||
755 | return Pagination.__super__.initialize.apply(this, arguments); | ||
756 | } | ||
757 | }); | ||
758 | return Pagination; | ||
759 | })(); | ||
760 | var Ordering = Backbone.Model.extend({ | ||
761 | defaults: { | ||
762 | value: null, | ||
763 | items: new Backbone.Collection(), | ||
764 | }, | ||
765 | initialize: function(data, options) { | ||
766 | if (this.get('value') === null) { | ||
767 | var firstItem = this.get('items').at(0); | ||
768 | if (firstItem) { | ||
769 | this.set('value', firstItem.get('value')); | ||
770 | } | ||
771 | } | ||
772 | }, | ||
773 | parse: function(data) { | ||
774 | var result = _.clone(data); | ||
775 | if (result.items && !(result.items instanceof Backbone.Collection)) { | ||
776 | result.items = new Backbone.Collection(result.items, {parse: true}); | ||
777 | } | ||
778 | return result; | ||
779 | }, | ||
780 | }); | ||
781 | var QueryTextField = Backbone.Model.extend({ | ||
782 | defaults: { | ||
783 | formName: null, | ||
784 | name: null, | ||
785 | queries: [], | ||
786 | fields: null, | ||
787 | multi: false | ||
788 | }, | ||
789 | }); | ||
790 | var SolrSearch = Pagination.extend({ | ||
791 | url: function url() { | ||
792 | return this.constructor.selectUrl; | ||
793 | }, | ||
794 | defaults: function defaults() { | ||
795 | var constructor = this.constructor; | ||
796 | var formNameMap = {}; | ||
797 | var facets = new SolrFacets(this.constructor.facets, {search: this}); | ||
798 | _.each(facets.values(), function(facet) { | ||
799 | var formName = facet.get('formName'); | ||
800 | if (formName) { | ||
801 | formNameMap[formName] = facet; | ||
802 | } | ||
803 | }); | ||
804 | var queryFields = new Backbone.Model(); | ||
805 | _.each(constructor.queryTextFields, function(definition, queryName) { | ||
806 | var qtf = new QueryTextField({formName: definition.formName, name: queryName, queries: [], fields: definition.fields, multi: !!definition.multi}); | ||
807 | var formName = qtf.get('formName'); | ||
808 | if (formName) { | ||
809 | formNameMap[formName] = qtf; | ||
810 | } | ||
811 | queryFields.set(queryName, qtf); | ||
812 | }, this); | ||
813 | return _.extend(_.result(SolrSearch.__super__, 'defaults'), { | ||
814 | initializing: true, | ||
815 | initialized: false, | ||
816 | query: '', | ||
817 | formNameMap: formNameMap, | ||
818 | results: new Backbone.Collection(), | ||
819 | ordering: new Ordering({items: constructor.orderingItems}, {parse: true}), | ||
820 | facets: facets, | ||
821 | queryFields: queryFields, | ||
822 | }); | ||
823 | }, | ||
824 | applyQueryParameters: function() { | ||
825 | var skipOptions = {skipSearch: true}; | ||
826 | var parts = document.location.href.match(/.*\?(.*)/); | ||
827 | var facets = this.get('facets'); | ||
828 | facets.resetSearch(); | ||
829 | _.each(this.get('queryFields').values(), function(qtf) { | ||
830 | qtf.set({ | ||
831 | query: null, | ||
832 | queries: [], | ||
833 | }, skipOptions); | ||
834 | }); | ||
835 | |||
836 | if (parts) { | ||
837 | var formNameMap = this.get('formNameMap'); | ||
838 | var keyValueParts = parts[1].split('&'); | ||
839 | _.each(keyValueParts, function(keyValuePart, i) { | ||
840 | var keyFieldValue = keyValuePart.match(/^([^.]+)(?:\.([^.]+))?=(.*)$/); | ||
841 | if (keyFieldValue) { | ||
842 | var key = keyFieldValue[1]; | ||
843 | var field = keyFieldValue[2]; | ||
844 | var value = keyFieldValue[3]; | ||
845 | value = value.replace(/(\+|%20)/g, ' '); | ||
846 | var impl = formNameMap[key]; | ||
847 | if (impl) { | ||
848 | if (impl instanceof QueryTextField) { | ||
849 | impl.set('query', value, skipOptions); | ||
850 | } else if (impl.facetType === 'field' && value) { | ||
851 | if (field === 'min') { | ||
852 | impl.set('queryMin', parseInt(value), skipOptions); | ||
853 | } else if (field === 'max') { | ||
854 | impl.set('queryMax', parseInt(value), skipOptions); | ||
855 | } else if (field === 'items') { | ||
856 | var items = impl.get('items'); | ||
857 | var item = items.get(value); | ||
858 | if (!item) { | ||
859 | item = new SolrFacets.Facet.Item({key: value, value: 0}); | ||
860 | items.add(item); | ||
861 | item.on('change:checked', function(model, value, options) { | ||
862 | this.trigger('item-change', null, null, options); | ||
863 | impl.trigger('item-change', null, null, options); | ||
864 | }, facets); | ||
865 | } | ||
866 | item.set('checked', true, skipOptions); | ||
867 | } | ||
868 | } | ||
869 | } | ||
870 | } | ||
871 | }, this); | ||
872 | } | ||
873 | }, | ||
874 | initialize: function(data, options) { | ||
875 | options = (options || {}); | ||
876 | this.set('options', new Backbone.Model({ | ||
877 | faceting: !!options.faceting, | ||
878 | highlighting: !!options.highlighting, | ||
879 | showAll: !!options.showAll, | ||
880 | })); | ||
881 | this._doSearchImmediately = _.bind(function(options) { | ||
882 | this.fetch(_.extend({}, options, { | ||
883 | data: this.toJSON(), | ||
884 | merge: false, | ||
885 | traditional: true, | ||
886 | })); | ||
887 | }, this); | ||
888 | this._doSearch = _.debounce(this._doSearchImmediately, 250); | ||
889 | this._doSearch = (function(doSearch) { | ||
890 | return function(options) { | ||
891 | var skipSearch = this._skipSearch || (options || {}).skipSearch; | ||
892 | if (!skipSearch) { | ||
893 | doSearch(options); | ||
894 | } | ||
895 | }; | ||
896 | })(this._doSearch); | ||
897 | _.each(this.get('queryFields').values(), function(subQueryModel) { | ||
898 | subQueryModel.on('change:query change:queries', function(model, value, options) { | ||
899 | this.trigger('change:queryFields', null, null, options); | ||
900 | }, this); | ||
901 | }, this); | ||
902 | this.on('change:queryFields', function(model, value, options) { | ||
903 | this.trigger('change:query', null, null, options); | ||
904 | }, this); | ||
905 | this.get('options').on('change:faceting change:highlighting', function(model, value, options) { | ||
906 | this._doSearch(options); | ||
907 | }, this); | ||
908 | this.on('change:query', function(model, value, options) { | ||
909 | // this is silent, which causes the change events to not fire; | ||
910 | // this is ok, because the next .on will also see the change | ||
911 | // event on query, and resend the search | ||
912 | this._doSearch(_.extend({}, options, {resetCurrentPage: true, resetFacets: true, resetOrdering: true})); | ||
913 | }, this); | ||
914 | this.on('change:pageSize', function(model, value, options) { | ||
915 | this._doSearch(_.extend({}, options, {resetCurrentPage: true})); | ||
916 | }, this); | ||
917 | this.on('change:currentPage', function(model, value, options) { | ||
918 | this._doSearch(options); | ||
919 | }, this); | ||
920 | this.get('facets').on('item-change', function(model, value, options) { | ||
921 | this._doSearch(_.extend({}, options, {resetCurrentPage: true})); | ||
922 | }, this); | ||
923 | this.get('ordering').on('change:value', function(model, value, options) { | ||
924 | this._doSearch(_.extend({}, options, {resetCurrentPage: true})); | ||
925 | }, this); | ||
926 | return SolrSearch.__super__.initialize.apply(this, arguments); | ||
927 | |||
928 | }, | ||
929 | parse: function parse(data, options) { | ||
930 | if (options.facetSuggestions) { | ||
931 | return null; | ||
932 | } | ||
933 | var skipOptions = _.extend({}, options, {skipSearch: true}); | ||
934 | if (options.resetCurrentPage) { | ||
935 | this.set('currentPage', 1, skipOptions); | ||
936 | } | ||
937 | if (options.resetFacets) { | ||
938 | this.get('facets').resetSearch(skipOptions); | ||
939 | } | ||
940 | if (options.resetOrdering) { | ||
941 | this.get('ordering').set('value', this.get('ordering').get('items').at(0).get('value'), skipOptions); | ||
942 | } | ||
943 | var facets = this.get('facets'); | ||
944 | if (this.get('options').get('faceting')) { | ||
945 | facets.applyFacetResults(data); | ||
946 | } else { | ||
947 | facets.resetSearch(options); | ||
948 | } | ||
949 | var list = []; | ||
950 | var highlighting = this.get('options').get('highlighting') ? data.highlighting : {}; | ||
951 | |||
952 | var self = this; | ||
953 | _.each(data.response.docs, function(doc, index) { | ||
954 | var itemHighlighting = highlighting[doc.id]; | ||
955 | if (!itemHighlighting) { | ||
956 | itemHighlighting = {}; | ||
957 | } | ||
958 | list.push(self.searchParseDoc(doc, index, itemHighlighting)); | ||
959 | }); | ||
960 | |||
961 | return { | ||
962 | initializing: false, | ||
963 | initialized: true, | ||
964 | results: new Backbone.Collection(list), | ||
965 | facets: facets, | ||
966 | options: this.get('options'), | ||
967 | totalCount: data.response.numFound, | ||
968 | queryTime: (data.responseHeader.QTime / 1000).toFixed(2), | ||
969 | hasResults: list.length > 0, | ||
970 | }; | ||
971 | }, | ||
972 | searchParseDoc: function searchParseDoc(doc, index, itemHighlighting) { | ||
973 | var fieldsToParse = mergeStaticProps(this.constructor, SolrSearch, {}, 'parsedFieldMap'); | ||
974 | var result = {}; | ||
975 | _.each(fieldsToParse, function(value, key) { | ||
976 | var parsed; | ||
977 | if (_.isFunction(value)) { | ||
978 | parsed = value.call(this, doc, index, itemHighlighting, key); | ||
979 | } else if (_.isArray(value)) { | ||
980 | parsed = []; | ||
981 | _.each(value, function(item, i) { | ||
982 | var rawValue = doc[item.name]; | ||
983 | if (rawValue) { | ||
984 | var parsedValue = item.parse.call(this, doc, index, itemHighlighting, rawValue, item.name); | ||
985 | if (parsedValue) { | ||
986 | parsed.push(parsedValue); | ||
987 | } | ||
988 | } | ||
989 | }, this); | ||
990 | } else { | ||
991 | parsed = doc[value]; | ||
992 | } | ||
993 | result[key] = parsed; | ||
994 | }, this); | ||
995 | return result; | ||
996 | }, | ||
997 | toJSON: function(options) { | ||
998 | if (!options) { | ||
999 | options = {}; | ||
1000 | } | ||
1001 | var facets = this.get('facets'); | ||
1002 | var constructor = this.constructor; | ||
1003 | var result = { | ||
1004 | defType: 'edismax', | ||
1005 | qf: constructor.queryField, | ||
1006 | wt: 'json', | ||
1007 | }; | ||
1008 | var ordering = this.get('ordering'); | ||
1009 | var sort = ordering.get('value'); | ||
1010 | if (options.resetOrdering) { | ||
1011 | sort = ordering.get('items').at(0).get('value'); | ||
1012 | } | ||
1013 | var currentPage = this.get('currentPage'); | ||
1014 | var pageSize = this.get('pageSize'); | ||
1015 | if (options.resetCurrentPage) { | ||
1016 | currentPage = 1; | ||
1017 | } | ||
1018 | result.sort = sort; | ||
1019 | result.rows = pageSize; | ||
1020 | result.start = (currentPage - 1) * pageSize; | ||
1021 | result.fl = mergeStaticSets(constructor, SolrSearch, [], 'returnFields'); | ||
1022 | if (this.get('options').get('highlighting')) { | ||
1023 | result.hl = true; | ||
1024 | result['hl.fl'] = ['content', 'title'].concat(constructor.extraHighlightFields); | ||
1025 | } | ||
1026 | |||
1027 | if (this.get('options').get('faceting')) { | ||
1028 | var facetFormData = facets.getFacetFormData(); | ||
1029 | var fq = facetFormData.fq; | ||
1030 | delete(facetFormData.fq); | ||
1031 | result = _.extend(result, facetFormData); | ||
1032 | if (fq.length) { | ||
1033 | if (result.fq) { | ||
1034 | result.fq = result.fq.concat(fq); | ||
1035 | } else { | ||
1036 | result.fq = fq; | ||
1037 | } | ||
1038 | } | ||
1039 | } | ||
1040 | |||
1041 | var queryParts = []; | ||
1042 | var queryFields = this.get('queryFields'); | ||
1043 | _.each(queryFields.keys(), function(queryName) { | ||
1044 | var subQueryModel = queryFields.get(queryName); | ||
1045 | var fields = subQueryModel.get('fields'); | ||
1046 | var queries = subQueryModel.get('queries'); | ||
1047 | var query = subQueryModel.get('query'); | ||
1048 | if (query) { | ||
1049 | queries = queries.concat([query]); | ||
1050 | } | ||
1051 | var subQuery = []; | ||
1052 | for (var i = 0; i < fields.length; i++) { | ||
1053 | var fieldName = fields[i]; | ||
1054 | for (var j = 0; j < queries.length; j++) { | ||
1055 | // FIXME: quote the query | ||
1056 | subQuery.push(fieldName + ':\'' + queries[j] + '\''); | ||
1057 | } | ||
1058 | } | ||
1059 | if (subQuery.length) { | ||
1060 | queryParts.push(subQuery.join(' OR ')); | ||
1061 | } | ||
1062 | }, this); | ||
1063 | |||
1064 | var query = queryParts.join(' AND '); | ||
1065 | if (!query) { | ||
1066 | query = '*:*'; | ||
1067 | if (!this.get('options').get('showAll') && !result.fq) { | ||
1068 | result.rows = 0; | ||
1069 | } | ||
1070 | } | ||
1071 | var dateBoostField = constructor.dateBoostField; | ||
1072 | var popularityBoostField = constructor.popularityBoostField; | ||
1073 | if (result.rows !== 0 && (dateBoostField || popularityBoostField)) { | ||
1074 | var boostSum = []; | ||
1075 | if (dateBoostField) { | ||
1076 | result.dateBoost = 'recip(ms(NOW,' + dateBoostField + '),3.16e-11,1,1)'; | ||
1077 | boostSum.push('$dateBoost'); | ||
1078 | } | ||
1079 | if (popularityBoostField) { | ||
1080 | result.popularityBoost = 'def(' + popularityBoostField + ',0)'; | ||
1081 | boostSum.push('$popularityBoost'); | ||
1082 | } | ||
1083 | result.qq = query; | ||
1084 | result.q = '{!boost b=sum(' + boostSum.join(',') + ') v=$qq defType=$defType}'; | ||
1085 | } else { | ||
1086 | result.q = query; | ||
1087 | } | ||
1088 | return result; | ||
1089 | }, | ||
1090 | }, { | ||
1091 | cleanup: function cleanup(txt) { | ||
1092 | if (txt) { | ||
1093 | txt = txt.replace(/&/g, '&'); | ||
1094 | txt = txt.replace(/'/g, '\''); | ||
1095 | txt = txt.replace(/[^a-zA-Z0-9 -\.,:;%<>\/'"|]/g, ' '); | ||
1096 | } | ||
1097 | return txt; | ||
1098 | }, | ||
1099 | returnFields: [ | ||
1100 | 'keywords', | ||
1101 | 'last_modified', | ||
1102 | 'path', | ||
1103 | 'score', | ||
1104 | 'title', | ||
1105 | ], | ||
1106 | parsedFieldMap: { | ||
1107 | content: function(doc, index, itemHighlighting) { | ||
1108 | var content = itemHighlighting.content; | ||
1109 | if (content && content.constructor === Array) { | ||
1110 | content = content.join(' '); | ||
1111 | } | ||
1112 | if (!content) { | ||
1113 | content = ''; | ||
1114 | } | ||
1115 | return this.constructor.cleanup(content); | ||
1116 | }, | ||
1117 | keywords: 'keywords', | ||
1118 | lastModified: 'last_modified', | ||
1119 | path: 'url', | ||
1120 | score: 'score', | ||
1121 | title: function(doc, index, itemHighlighting) { | ||
1122 | var title; | ||
1123 | if (itemHighlighting.title) { | ||
1124 | title = itemHighlighting.title.join(' '); | ||
1125 | } else { | ||
1126 | title = doc.title[0]; | ||
1127 | } | ||
1128 | return this.constructor.cleanup(title); | ||
1129 | }, | ||
1130 | }, | ||
1131 | }); | ||
1132 | SolrSearch.Facets = SolrFacets; | ||
1133 | SolrSearch.Pagination = Pagination; | ||
1134 | return SolrSearch; | ||
1135 | }); | 590 | }); | ... | ... |
src/scripts/solr/model/Ordering.js
0 → 100644
1 | define(function(require) { | ||
2 | 'use strict'; | ||
3 | var Backbone = require('backbone'); | ||
4 | |||
5 | var Ordering = Backbone.Model.extend({ | ||
6 | defaults: { | ||
7 | value: null, | ||
8 | items: new Backbone.Collection(), | ||
9 | }, | ||
10 | initialize: function(data, options) { | ||
11 | if (this.get('value') === null) { | ||
12 | var firstItem = this.get('items').at(0); | ||
13 | if (firstItem) { | ||
14 | this.set('value', firstItem.get('value')); | ||
15 | } | ||
16 | } | ||
17 | }, | ||
18 | parse: function(data) { | ||
19 | var result = _.clone(data); | ||
20 | if (result.items && !(result.items instanceof Backbone.Collection)) { | ||
21 | result.items = new Backbone.Collection(result.items, {parse: true}); | ||
22 | } | ||
23 | return result; | ||
24 | }, | ||
25 | }); | ||
26 | return Ordering; | ||
27 | }); |
src/scripts/solr/model/Pagination.js
0 → 100644
1 | define(function(require) { | ||
2 | 'use strict'; | ||
3 | var Backbone = require('backbone'); | ||
4 | |||
5 | function stepFalse(e) { | ||
6 | e.preventDefault(); | ||
7 | return false; | ||
8 | } | ||
9 | var Pagination = Backbone.Model.extend({ | ||
10 | defaults: { | ||
11 | currentPage: 1, | ||
12 | hasNext: false, | ||
13 | hasNextJumpPage: false, | ||
14 | hasPrevious: false, | ||
15 | hasPreviousJumpPage: false, | ||
16 | nextJumpPage: undefined, | ||
17 | nextPage: undefined, | ||
18 | pages: [], | ||
19 | pageJump: 5, | ||
20 | pageSize: 10, | ||
21 | previousJumpPage: undefined, | ||
22 | previousPage: undefined, | ||
23 | totalCount: 0, | ||
24 | totalPages: 0, | ||
25 | }, | ||
26 | initialize: function(data, options) { | ||
27 | var buildPages = function buildPages() { | ||
28 | var currentPage = parseInt(this.get('currentPage')); | ||
29 | var pageSize = parseInt(this.get('pageSize')); | ||
30 | var totalCount = parseInt(this.get('totalCount')); | ||
31 | if (!currentPage || !pageSize || !totalCount) { | ||
32 | return; | ||
33 | } | ||
34 | var pages = []; | ||
35 | var totalPages = Math.floor((totalCount + pageSize - 1) / pageSize); | ||
36 | function addPage(self, i) { | ||
37 | pages.push({ | ||
38 | current: i === currentPage, | ||
39 | jump: function() { | ||
40 | self.set('currentPage', i); | ||
41 | return true; | ||
42 | }, | ||
43 | number: i, | ||
44 | }); | ||
45 | } | ||
46 | var startAt = currentPage - 4, endAt = currentPage + 5; | ||
47 | if (startAt < 1) { | ||
48 | endAt += (1 - startAt); | ||
49 | } | ||
50 | if (endAt > totalPages) { | ||
51 | startAt -= endAt - totalPages - 1; | ||
52 | endAt = totalPages + 1; | ||
53 | } | ||
54 | if (startAt < 1) { | ||
55 | startAt = 1; | ||
56 | } | ||
57 | if (endAt - startAt < 9) { | ||
58 | /* global console:false */ | ||
59 | console.log('foo'); | ||
60 | } | ||
61 | |||
62 | for (var i = startAt; i < endAt; i++) { | ||
63 | if (i > 0 && i <= totalPages) { | ||
64 | addPage(this, i); | ||
65 | } | ||
66 | } | ||
67 | var hasPrevious = currentPage > 1; | ||
68 | var hasNext = currentPage < totalPages; | ||
69 | var pageJump = this.get('pageJump'); | ||
70 | var nextJumpPage, previousJumpPage, hasNextJump, hasPreviousJump; | ||
71 | if (pageJump) { | ||
72 | nextJumpPage = currentPage + pageJump; | ||
73 | previousJumpPage = currentPage - pageJump; | ||
74 | hasNextJump = nextJumpPage < totalPages; | ||
75 | hasPreviousJump = previousJumpPage > 0; | ||
76 | } else { | ||
77 | hasNextJump = false; | ||
78 | hasPreviousJump = false; | ||
79 | } | ||
80 | this.set({ | ||
81 | hasNext: hasNext, | ||
82 | hasNextJump: hasNextJump, | ||
83 | hasPrevious: hasPrevious, | ||
84 | hasPreviousJump: hasPreviousJump, | ||
85 | nextJumpPage: hasNextJump ? nextJumpPage : undefined, | ||
86 | nextPage: hasNext ? currentPage + 1 : undefined, | ||
87 | pages: pages, | ||
88 | previousJumpPage: hasNextJump ? previousJumpPage : undefined, | ||
89 | previousPage: hasPrevious ? currentPage - 1 : undefined, | ||
90 | totalPages: totalPages, | ||
91 | }); | ||
92 | }; | ||
93 | this.on('change:pageSize change:currentPage change:totalCount', buildPages, this); | ||
94 | var installActions = _.bind(function installActions(eventName, nextName, hasNextName, previousName, hasPreviousName, pageCount) { | ||
95 | this.on(eventName, function() { | ||
96 | var hasNext = this.get(hasNextName); | ||
97 | var hasPrevious = this.get(hasPreviousName); | ||
98 | var next, previous; | ||
99 | if (hasNext) { | ||
100 | next = _.bind(function(e) { | ||
101 | e.preventDefault(); | ||
102 | this.set('currentPage', this.get('currentPage') + pageCount); | ||
103 | return false; | ||
104 | }, this); | ||
105 | } else { | ||
106 | next = stepFalse; | ||
107 | } | ||
108 | if (hasPrevious) { | ||
109 | previous = _.bind(function(e) { | ||
110 | e.preventDefault(); | ||
111 | this.set('currentPage', this.get('currentPage') - pageCount); | ||
112 | return false; | ||
113 | }, this); | ||
114 | } else { | ||
115 | previous = stepFalse; | ||
116 | } | ||
117 | this.set(nextName, next); | ||
118 | this.set(previousName, previous); | ||
119 | }, this); | ||
120 | this[nextName] = _.bind(function() { | ||
121 | return this.get(nextName)(); | ||
122 | }, this); | ||
123 | this[previousName] = _.bind(function() { | ||
124 | return this.get(previousName)(); | ||
125 | }, this); | ||
126 | }, this); | ||
127 | this.on('change:pageJump', function() { | ||
128 | var pageJump = this.get('pageJump'); | ||
129 | installActions('change:hasNextJump change:hasPreviousJump', 'nextJump', 'hasNextJump', 'previousJump', 'hasPreviousJump', pageJump); | ||
130 | }, this); | ||
131 | installActions('change:hasNext change:hasPrevious', 'next', 'hasNext', 'previous', 'hasPrevious', 1); | ||
132 | buildPages.apply(this); | ||
133 | this.trigger('change:pageJump'); | ||
134 | return Pagination.__super__.initialize.apply(this, arguments); | ||
135 | } | ||
136 | }); | ||
137 | return Pagination; | ||
138 | }); |
src/scripts/solr/model/QueryTextField.js
0 → 100644
src/scripts/solr/model/Solr.js
0 → 100644
1 | define(function(require) { | ||
2 | 'use strict'; | ||
3 | var _ = require('underscore'); | ||
4 | var Backbone = require('backbone'); | ||
5 | |||
6 | var Facets = require('solr/model/Facets'); | ||
7 | var Ordering = require('solr/model/Ordering'); | ||
8 | var Pagination = require('solr/model/Pagination'); | ||
9 | var QueryTextField = require('solr/model/QueryTextField'); | ||
10 | |||
11 | //var module = require('module'); | ||
12 | |||
13 | function mergeStatic(startPtr, endPtr, obj, fieldName, filterFunc) { | ||
14 | var result = filterFunc(obj); | ||
15 | var ptr = startPtr; | ||
16 | while (true) { | ||
17 | result = _.extend(result, filterFunc(_.result(ptr, fieldName))); | ||
18 | if (ptr === endPtr) { | ||
19 | break; | ||
20 | } | ||
21 | ptr = ptr.__super__.constructor; | ||
22 | } | ||
23 | return result; | ||
24 | } | ||
25 | |||
26 | function mergeStaticProps(startPtr, endPtr, obj, fieldName) { | ||
27 | return mergeStaticFunc(startPtr, endPtr, obj, fieldName, _.identity); | ||
28 | } | ||
29 | |||
30 | function mergeStaticSets(startPtr, endPtr, obj, fieldName) { | ||
31 | return _.keys(mergeStaticFunc(startPtr, endPtr, obj, fieldName, function arrayToMap(array) { | ||
32 | var result = {}; | ||
33 | _.each(array, function(value) { | ||
34 | result[value] = true; | ||
35 | }); | ||
36 | return result; | ||
37 | })); | ||
38 | } | ||
39 | |||
40 | function getItemKeyAccessor(item) { | ||
41 | return item.get('key'); | ||
42 | } | ||
43 | var Solr = Pagination.extend({ | ||
44 | url: function url() { | ||
45 | return this.constructor.selectUrl; | ||
46 | }, | ||
47 | defaults: function defaults() { | ||
48 | var constructor = this.constructor; | ||
49 | var formNameMap = {}; | ||
50 | var facets = new Facets(this.constructor.facets, {search: this}); | ||
51 | _.each(facets.values(), function(facet) { | ||
52 | var formName = facet.get('formName'); | ||
53 | if (formName) { | ||
54 | formNameMap[formName] = facet; | ||
55 | } | ||
56 | }); | ||
57 | var queryFields = new Backbone.Model(); | ||
58 | _.each(constructor.queryTextFields, function(definition, queryName) { | ||
59 | var qtf = new QueryTextField({formName: definition.formName, name: queryName, queries: [], fields: definition.fields, multi: !!definition.multi}); | ||
60 | var formName = qtf.get('formName'); | ||
61 | if (formName) { | ||
62 | formNameMap[formName] = qtf; | ||
63 | } | ||
64 | queryFields.set(queryName, qtf); | ||
65 | }, this); | ||
66 | return _.extend(_.result(Solr.__super__, 'defaults'), { | ||
67 | initializing: true, | ||
68 | initialized: false, | ||
69 | query: '', | ||
70 | formNameMap: formNameMap, | ||
71 | results: new Backbone.Collection(), | ||
72 | ordering: new Ordering({items: constructor.orderingItems}, {parse: true}), | ||
73 | facets: facets, | ||
74 | queryFields: queryFields, | ||
75 | }); | ||
76 | }, | ||
77 | applyQueryParameters: function() { | ||
78 | var skipOptions = {skipSearch: true}; | ||
79 | var parts = document.location.href.match(/.*\?(.*)/); | ||
80 | var facets = this.get('facets'); | ||
81 | facets.resetSearch(); | ||
82 | _.each(this.get('queryFields').values(), function(qtf) { | ||
83 | qtf.set({ | ||
84 | query: null, | ||
85 | queries: [], | ||
86 | }, skipOptions); | ||
87 | }); | ||
88 | |||
89 | if (parts) { | ||
90 | var formNameMap = this.get('formNameMap'); | ||
91 | var keyValueParts = parts[1].split('&'); | ||
92 | _.each(keyValueParts, function(keyValuePart, i) { | ||
93 | var keyFieldValue = keyValuePart.match(/^([^.]+)(?:\.([^.]+))?=(.*)$/); | ||
94 | if (keyFieldValue) { | ||
95 | var key = keyFieldValue[1]; | ||
96 | var field = keyFieldValue[2]; | ||
97 | var value = keyFieldValue[3]; | ||
98 | value = value.replace(/(\+|%20)/g, ' '); | ||
99 | var impl = formNameMap[key]; | ||
100 | if (impl) { | ||
101 | if (impl instanceof QueryTextField) { | ||
102 | impl.set('query', value, skipOptions); | ||
103 | } else if (impl.facetType === 'field' && value) { | ||
104 | if (field === 'min') { | ||
105 | impl.set('queryMin', parseInt(value), skipOptions); | ||
106 | } else if (field === 'max') { | ||
107 | impl.set('queryMax', parseInt(value), skipOptions); | ||
108 | } else if (field === 'items') { | ||
109 | var items = impl.get('items'); | ||
110 | var item = items.get(value); | ||
111 | if (!item) { | ||
112 | item = new Facets.Facet.Item({key: value, value: 0}); | ||
113 | items.add(item); | ||
114 | item.on('change:checked', function(model, value, options) { | ||
115 | this.trigger('item-change', null, null, options); | ||
116 | impl.trigger('item-change', null, null, options); | ||
117 | }, facets); | ||
118 | } | ||
119 | item.set('checked', true, skipOptions); | ||
120 | } | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | }, this); | ||
125 | } | ||
126 | }, | ||
127 | initialize: function(data, options) { | ||
128 | options = (options || {}); | ||
129 | this.set('options', new Backbone.Model({ | ||
130 | faceting: !!options.faceting, | ||
131 | highlighting: !!options.highlighting, | ||
132 | showAll: !!options.showAll, | ||
133 | })); | ||
134 | this._doSearchImmediately = _.bind(function(options) { | ||
135 | this.fetch(_.extend({}, options, { | ||
136 | data: this.toJSON(), | ||
137 | merge: false, | ||
138 | traditional: true, | ||
139 | })); | ||
140 | }, this); | ||
141 | this._doSearch = _.debounce(this._doSearchImmediately, 250); | ||
142 | this._doSearch = (function(doSearch) { | ||
143 | return function(options) { | ||
144 | var skipSearch = this._skipSearch || (options || {}).skipSearch; | ||
145 | if (!skipSearch) { | ||
146 | doSearch(options); | ||
147 | } | ||
148 | }; | ||
149 | })(this._doSearch); | ||
150 | _.each(this.get('queryFields').values(), function(subQueryModel) { | ||
151 | subQueryModel.on('change:query change:queries', function(model, value, options) { | ||
152 | this.trigger('change:queryFields', null, null, options); | ||
153 | }, this); | ||
154 | }, this); | ||
155 | this.on('change:queryFields', function(model, value, options) { | ||
156 | this.trigger('change:query', null, null, options); | ||
157 | }, this); | ||
158 | this.get('options').on('change:faceting change:highlighting', function(model, value, options) { | ||
159 | this._doSearch(options); | ||
160 | }, this); | ||
161 | this.on('change:query', function(model, value, options) { | ||
162 | // this is silent, which causes the change events to not fire; | ||
163 | // this is ok, because the next .on will also see the change | ||
164 | // event on query, and resend the search | ||
165 | this._doSearch(_.extend({}, options, {resetCurrentPage: true, resetFacets: true, resetOrdering: true})); | ||
166 | }, this); | ||
167 | this.on('change:pageSize', function(model, value, options) { | ||
168 | this._doSearch(_.extend({}, options, {resetCurrentPage: true})); | ||
169 | }, this); | ||
170 | this.on('change:currentPage', function(model, value, options) { | ||
171 | this._doSearch(options); | ||
172 | }, this); | ||
173 | this.get('facets').on('item-change', function(model, value, options) { | ||
174 | this._doSearch(_.extend({}, options, {resetCurrentPage: true})); | ||
175 | }, this); | ||
176 | this.get('ordering').on('change:value', function(model, value, options) { | ||
177 | this._doSearch(_.extend({}, options, {resetCurrentPage: true})); | ||
178 | }, this); | ||
179 | return Solr.__super__.initialize.apply(this, arguments); | ||
180 | |||
181 | }, | ||
182 | parse: function parse(data, options) { | ||
183 | if (options.facetSuggestions) { | ||
184 | return null; | ||
185 | } | ||
186 | var skipOptions = _.extend({}, options, {skipSearch: true}); | ||
187 | if (options.resetCurrentPage) { | ||
188 | this.set('currentPage', 1, skipOptions); | ||
189 | } | ||
190 | if (options.resetFacets) { | ||
191 | this.get('facets').resetSearch(skipOptions); | ||
192 | } | ||
193 | if (options.resetOrdering) { | ||
194 | this.get('ordering').set('value', this.get('ordering').get('items').at(0).get('value'), skipOptions); | ||
195 | } | ||
196 | var facets = this.get('facets'); | ||
197 | if (this.get('options').get('faceting')) { | ||
198 | facets.applyFacetResults(data); | ||
199 | } else { | ||
200 | facets.resetSearch(options); | ||
201 | } | ||
202 | var list = []; | ||
203 | var highlighting = this.get('options').get('highlighting') ? data.highlighting : {}; | ||
204 | |||
205 | var self = this; | ||
206 | _.each(data.response.docs, function(doc, index) { | ||
207 | var itemHighlighting = highlighting[doc.id]; | ||
208 | if (!itemHighlighting) { | ||
209 | itemHighlighting = {}; | ||
210 | } | ||
211 | list.push(self.searchParseDoc(doc, index, itemHighlighting)); | ||
212 | }); | ||
213 | |||
214 | return { | ||
215 | initializing: false, | ||
216 | initialized: true, | ||
217 | results: new Backbone.Collection(list), | ||
218 | facets: facets, | ||
219 | options: this.get('options'), | ||
220 | totalCount: data.response.numFound, | ||
221 | queryTime: (data.responseHeader.QTime / 1000).toFixed(2), | ||
222 | hasResults: list.length > 0, | ||
223 | }; | ||
224 | }, | ||
225 | searchParseDoc: function searchParseDoc(doc, index, itemHighlighting) { | ||
226 | var fieldsToParse = mergeStaticProps(this.constructor, Solr, {}, 'parsedFieldMap'); | ||
227 | var result = {}; | ||
228 | _.each(fieldsToParse, function(value, key) { | ||
229 | var parsed; | ||
230 | if (_.isFunction(value)) { | ||
231 | parsed = value.call(this, doc, index, itemHighlighting, key); | ||
232 | } else if (_.isArray(value)) { | ||
233 | parsed = []; | ||
234 | _.each(value, function(item, i) { | ||
235 | var rawValue = doc[item.name]; | ||
236 | if (rawValue) { | ||
237 | var parsedValue = item.parse.call(this, doc, index, itemHighlighting, rawValue, item.name); | ||
238 | if (parsedValue) { | ||
239 | parsed.push(parsedValue); | ||
240 | } | ||
241 | } | ||
242 | }, this); | ||
243 | } else { | ||
244 | parsed = doc[value]; | ||
245 | } | ||
246 | result[key] = parsed; | ||
247 | }, this); | ||
248 | return result; | ||
249 | }, | ||
250 | toJSON: function(options) { | ||
251 | if (!options) { | ||
252 | options = {}; | ||
253 | } | ||
254 | var facets = this.get('facets'); | ||
255 | var constructor = this.constructor; | ||
256 | var result = { | ||
257 | defType: 'edismax', | ||
258 | qf: constructor.queryField, | ||
259 | wt: 'json', | ||
260 | }; | ||
261 | var ordering = this.get('ordering'); | ||
262 | var sort = ordering.get('value'); | ||
263 | if (options.resetOrdering) { | ||
264 | sort = ordering.get('items').at(0).get('value'); | ||
265 | } | ||
266 | var currentPage = this.get('currentPage'); | ||
267 | var pageSize = this.get('pageSize'); | ||
268 | if (options.resetCurrentPage) { | ||
269 | currentPage = 1; | ||
270 | } | ||
271 | result.sort = sort; | ||
272 | result.rows = pageSize; | ||
273 | result.start = (currentPage - 1) * pageSize; | ||
274 | result.fl = mergeStaticSets(constructor, Solr, [], 'returnFields'); | ||
275 | if (this.get('options').get('highlighting')) { | ||
276 | result.hl = true; | ||
277 | result['hl.fl'] = ['content', 'title'].concat(constructor.extraHighlightFields); | ||
278 | } | ||
279 | |||
280 | if (this.get('options').get('faceting')) { | ||
281 | var facetFormData = facets.getFacetFormData(); | ||
282 | var fq = facetFormData.fq; | ||
283 | delete(facetFormData.fq); | ||
284 | result = _.extend(result, facetFormData); | ||
285 | if (fq.length) { | ||
286 | if (result.fq) { | ||
287 | result.fq = result.fq.concat(fq); | ||
288 | } else { | ||
289 | result.fq = fq; | ||
290 | } | ||
291 | } | ||
292 | } | ||
293 | |||
294 | var queryParts = []; | ||
295 | var queryFields = this.get('queryFields'); | ||
296 | _.each(queryFields.keys(), function(queryName) { | ||
297 | var subQueryModel = queryFields.get(queryName); | ||
298 | var fields = subQueryModel.get('fields'); | ||
299 | var queries = subQueryModel.get('queries'); | ||
300 | var query = subQueryModel.get('query'); | ||
301 | if (query) { | ||
302 | queries = queries.concat([query]); | ||
303 | } | ||
304 | var subQuery = []; | ||
305 | for (var i = 0; i < fields.length; i++) { | ||
306 | var fieldName = fields[i]; | ||
307 | for (var j = 0; j < queries.length; j++) { | ||
308 | // FIXME: quote the query | ||
309 | subQuery.push(fieldName + ':\'' + queries[j] + '\''); | ||
310 | } | ||
311 | } | ||
312 | if (subQuery.length) { | ||
313 | queryParts.push(subQuery.join(' OR ')); | ||
314 | } | ||
315 | }, this); | ||
316 | |||
317 | var query = queryParts.join(' AND '); | ||
318 | if (!query) { | ||
319 | query = '*:*'; | ||
320 | if (!this.get('options').get('showAll') && !result.fq) { | ||
321 | result.rows = 0; | ||
322 | } | ||
323 | } | ||
324 | var dateBoostField = constructor.dateBoostField; | ||
325 | var popularityBoostField = constructor.popularityBoostField; | ||
326 | if (result.rows !== 0 && (dateBoostField || popularityBoostField)) { | ||
327 | var boostSum = []; | ||
328 | if (dateBoostField) { | ||
329 | result.dateBoost = 'recip(ms(NOW,' + dateBoostField + '),3.16e-11,1,1)'; | ||
330 | boostSum.push('$dateBoost'); | ||
331 | } | ||
332 | if (popularityBoostField) { | ||
333 | result.popularityBoost = 'def(' + popularityBoostField + ',0)'; | ||
334 | boostSum.push('$popularityBoost'); | ||
335 | } | ||
336 | result.qq = query; | ||
337 | result.q = '{!boost b=sum(' + boostSum.join(',') + ') v=$qq defType=$defType}'; | ||
338 | } else { | ||
339 | result.q = query; | ||
340 | } | ||
341 | return result; | ||
342 | }, | ||
343 | }, { | ||
344 | cleanup: function cleanup(txt) { | ||
345 | if (txt) { | ||
346 | txt = txt.replace(/&/g, '&'); | ||
347 | txt = txt.replace(/'/g, '\''); | ||
348 | txt = txt.replace(/[^a-zA-Z0-9 -\.,:;%<>\/'"|]/g, ' '); | ||
349 | } | ||
350 | return txt; | ||
351 | }, | ||
352 | returnFields: [ | ||
353 | 'keywords', | ||
354 | 'last_modified', | ||
355 | 'path', | ||
356 | 'score', | ||
357 | 'title', | ||
358 | ], | ||
359 | parsedFieldMap: { | ||
360 | content: function(doc, index, itemHighlighting) { | ||
361 | var content = itemHighlighting.content; | ||
362 | if (content && content.constructor === Array) { | ||
363 | content = content.join(' '); | ||
364 | } | ||
365 | if (!content) { | ||
366 | content = ''; | ||
367 | } | ||
368 | return this.constructor.cleanup(content); | ||
369 | }, | ||
370 | keywords: 'keywords', | ||
371 | lastModified: 'last_modified', | ||
372 | path: 'url', | ||
373 | score: 'score', | ||
374 | title: function(doc, index, itemHighlighting) { | ||
375 | var title; | ||
376 | if (itemHighlighting.title) { | ||
377 | title = itemHighlighting.title.join(' '); | ||
378 | } else { | ||
379 | title = doc.title[0]; | ||
380 | } | ||
381 | return this.constructor.cleanup(title); | ||
382 | }, | ||
383 | }, | ||
384 | }); | ||
385 | Solr.Facets = Facets; | ||
386 | Solr.Pagination = Pagination; | ||
387 | return Solr; | ||
388 | }); |
-
Please register or sign in to post a comment