Implements the search part

This commit is contained in:
2016-01-26 12:31:03 +01:00
parent 5f2208839d
commit f6a884b188
6 changed files with 100 additions and 15 deletions

View File

@@ -21,9 +21,11 @@ func (s *BleveIndexerSuite) SetUpSuite(c *C) {
start := time.Now()
s.i, err = bleve.New(filepath.Join(c.MkDir(), "satbd-test.bar.satellite"), buildAlbumMapping())
c.Assert(err, IsNil)
batch := s.i.NewBatch()
for _, a := range albumsDataTest {
c.Assert(s.i.Index(AlbumIDString(a.ID), &a), IsNil)
c.Assert(batch.Index(AlbumIDString(a.ID), &a), IsNil)
}
c.Assert(s.i.Batch(batch), IsNil)
log.Printf("Indexing took %s", time.Since(start))
}

15
main.go
View File

@@ -57,13 +57,14 @@ func newAppData(opts Options) (*appData, error) {
res := &appData{
opts: opts,
errors: make(chan error, 10),
errors: make(chan error),
}
blevePath := filepath.Join(basepath, "index")
res.index, err = bleve.Open(blevePath)
if err == bleve.ErrorIndexPathDoesNotExist {
log.Printf("Creating a new index in %s", blevePath)
res.index, err = bleve.New(blevePath, buildAlbumMapping())
if err != nil {
return nil, err
@@ -151,8 +152,8 @@ func (a *appData) readCsv(stopChan <-chan struct{},
func (a *appData) submitBatchToIndex(batch *bleve.Batch) {
s := batch.Size()
log.Printf("[INDEX] start indexing of %d albums", s)
err := a.index.Batch(batch)
start := time.Now()
err := a.index.Batch(batch)
if err != nil {
a.errors <- fmt.Errorf("[INDEX] batch indexing failed: %s", err)
return
@@ -181,12 +182,13 @@ func (a *appData) indexAlbum(stopChan <-chan struct{},
break
}
}
err := batch.Index(AlbumIDString(album.ID), a)
err := batch.Index(AlbumIDString(album.ID), album)
if err != nil {
a.errors <- fmt.Errorf("[INDEX] could not batch indexion of %s: %s", album, err)
break
} else {
count++
}
count++
if count%a.opts.BatchSize == 0 {
a.submitBatchToIndex(batch)
batch = a.index.NewBatch()
@@ -196,7 +198,7 @@ func (a *appData) indexAlbum(stopChan <-chan struct{},
albumToReIndex = nil
break
}
err := a.index.Index(AlbumIDString(album.ID), a)
err := a.index.Index(AlbumIDString(album.ID), album)
if err != nil {
a.errors <- fmt.Errorf("[INDEX] re-indexing %s failed: %s", album, err)
}
@@ -215,6 +217,7 @@ func (a *appData) indexAlbum(stopChan <-chan struct{},
}
func (a *appData) cacheAlbumDescription(getAlbum <-chan *Album, toIndex chan<- *Album) {
defer close(toIndex)
nbAlbums := 0
for album := range getAlbum {
nbAlbums++
@@ -323,7 +326,7 @@ func (a *appData) updateCache(stopChan <-chan struct{}, periode time.Duration, c
func (a *appData) logErrors() {
for e := range a.errors {
log.Printf("%s", e)
log.Printf("[errors] ]%s", e)
}
}

View File

@@ -34,7 +34,6 @@ func (a *appData) buildRouter() http.Handler {
bleve_http.RegisterIndexName("album", a.index)
searchHandler := bleve_http.NewSearchHandler("album")
router.POST("/api/search", narco.EndChain(ch,
narco.HandlerFunc(func(_ context.Context, w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
searchHandler.ServeHTTP(w, req)

View File

@@ -38,9 +38,9 @@
<li ng-class="{active: isActive('collections')}"><a ng-click="collections()">Par Collection</a></li>
<li ng-class="{active: isActive('authors')}"><a ng-click="authors()">Par Auteurs</a></li>
</ul>
<form class="navbar-form navbar-right" role="search">
<form class="navbar-form navbar-right" role="search" ng-submit="search()">
<div class="input-group">
<input type="text" class="form-control" placeholder="Chercher ...">
<input type="text" class="form-control" ng-model="searchQuery" placeholder="Chercher ...">
<div class="input-group-btn">
<button class="btn btn-default" type="submit">Go!</button>
<button type="button" class="btn btn-default" ng-click="openModal('lg')">
@@ -55,6 +55,31 @@
<!-- Begin page content -->
<div class="container-fluid" >
<!-- search results -->
<div class="search row" ng-controller="SearchCtrl" ng-cloak ng-show="showResults">
<h3> La recherche à pris {{searchResults.took/1000000000}} secondes </h3>
<div class="col-xs-12" ng-repeat="hit in searchResults.hits">
<h3> {{hit.fields["série"]}} - {{hit.fields.titre}} </h3>
<ul>
<li><label>Référence:</label>{{hit.fields.ref}}</li>
<li><label>Description:</label>{{hit.fields.description}}</li>
<li><label>Collection:</label> {{hit.fields.collection}}</li>
<li><label>Note:</label> {{hit.fields.Note}} / 5.0 </li>
</ul>
<ul>
<li ng-repeat="(name,fragments) in hit.fragments" ><label>{{name}}</label>
<ul>
<li ng-repeat="f in fragments" ng-bind-html="f"></li>
</ul>
</li>
</div>
<!-- <p> <label>JSON:</label> {{searchResultsJSON}} </p>-->
</div>
</div>
<!-- Recent Albums -->
<div class="recent row" ng-controller="RecentCtrl" ng-cloak>
<div class="album col-xs-6 col-sm-4 col-md-3 col-lg-2" ng-repeat="id in albumIDs" ng-controller="RecentAlbumCtrl" ng-init="init_by_id(id)">
<img class="cover img-responsive img-rounded center-block" ng-src="{{album.CoverURL}}">
@@ -64,6 +89,7 @@
<script src="/js/angular.min.js"></script>
<script src="/js/angular-animate.min.js"></script>
<script src="/js/angular-sanitize.min.js"></script>
<script src="/js/ui-bootstrap-tpls-1.1.0.min.js"></script>
<script src="/js/satbd.satellite.bar.js"></script>
</body>

16
static/js/angular-sanitize.min.js vendored Normal file
View File

@@ -0,0 +1,16 @@
/*
AngularJS v1.4.8
(c) 2010-2015 Google, Inc. http://angularjs.org
License: MIT
*/
(function(n,h,p){'use strict';function E(a){var f=[];r(f,h.noop).chars(a);return f.join("")}function g(a,f){var d={},c=a.split(","),b;for(b=0;b<c.length;b++)d[f?h.lowercase(c[b]):c[b]]=!0;return d}function F(a,f){function d(a,b,d,l){b=h.lowercase(b);if(s[b])for(;e.last()&&t[e.last()];)c("",e.last());u[b]&&e.last()==b&&c("",b);(l=v[b]||!!l)||e.push(b);var m={};d.replace(G,function(b,a,f,c,d){m[a]=q(f||c||d||"")});f.start&&f.start(b,m,l)}function c(b,a){var c=0,d;if(a=h.lowercase(a))for(c=e.length-
1;0<=c&&e[c]!=a;c--);if(0<=c){for(d=e.length-1;d>=c;d--)f.end&&f.end(e[d]);e.length=c}}"string"!==typeof a&&(a=null===a||"undefined"===typeof a?"":""+a);var b,k,e=[],m=a,l;for(e.last=function(){return e[e.length-1]};a;){l="";k=!0;if(e.last()&&w[e.last()])a=a.replace(new RegExp("([\\W\\w]*)<\\s*\\/\\s*"+e.last()+"[^>]*>","i"),function(a,b){b=b.replace(H,"$1").replace(I,"$1");f.chars&&f.chars(q(b));return""}),c("",e.last());else{if(0===a.indexOf("\x3c!--"))b=a.indexOf("--",4),0<=b&&a.lastIndexOf("--\x3e",
b)===b&&(f.comment&&f.comment(a.substring(4,b)),a=a.substring(b+3),k=!1);else if(x.test(a)){if(b=a.match(x))a=a.replace(b[0],""),k=!1}else if(J.test(a)){if(b=a.match(y))a=a.substring(b[0].length),b[0].replace(y,c),k=!1}else K.test(a)&&((b=a.match(z))?(b[4]&&(a=a.substring(b[0].length),b[0].replace(z,d)),k=!1):(l+="<",a=a.substring(1)));k&&(b=a.indexOf("<"),l+=0>b?a:a.substring(0,b),a=0>b?"":a.substring(b),f.chars&&f.chars(q(l)))}if(a==m)throw L("badparse",a);m=a}c()}function q(a){if(!a)return"";A.innerHTML=
a.replace(/</g,"&lt;");return A.textContent}function B(a){return a.replace(/&/g,"&amp;").replace(M,function(a){var d=a.charCodeAt(0);a=a.charCodeAt(1);return"&#"+(1024*(d-55296)+(a-56320)+65536)+";"}).replace(N,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"&lt;").replace(/>/g,"&gt;")}function r(a,f){var d=!1,c=h.bind(a,a.push);return{start:function(a,k,e){a=h.lowercase(a);!d&&w[a]&&(d=a);d||!0!==C[a]||(c("<"),c(a),h.forEach(k,function(d,e){var k=h.lowercase(e),g="img"===a&&"src"===k||
"background"===k;!0!==O[k]||!0===D[k]&&!f(d,g)||(c(" "),c(e),c('="'),c(B(d)),c('"'))}),c(e?"/>":">"))},end:function(a){a=h.lowercase(a);d||!0!==C[a]||(c("</"),c(a),c(">"));a==d&&(d=!1)},chars:function(a){d||c(B(a))}}}var L=h.$$minErr("$sanitize"),z=/^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,y=/^<\/\s*([\w:-]+)[^>]*>/,G=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,K=/^</,J=/^<\//,H=/\x3c!--(.*?)--\x3e/g,x=/<!DOCTYPE([^>]*?)>/i,
I=/<!\[CDATA\[(.*?)]]\x3e/g,M=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,N=/([^\#-~| |!])/g,v=g("area,br,col,hr,img,wbr");n=g("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr");p=g("rp,rt");var u=h.extend({},p,n),s=h.extend({},n,g("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),t=h.extend({},p,g("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
n=g("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,stop,svg,switch,text,title,tspan,use");var w=g("script,style"),C=h.extend({},v,s,t,u,n),D=g("background,cite,href,longdesc,src,usemap,xlink:href");n=g("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,valign,value,vspace,width");
p=g("accent-height,accumulate,additive,alphabetic,arabic-form,ascent,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan",
!0);var O=h.extend({},D,p,n),A=document.createElement("pre");h.module("ngSanitize",[]).provider("$sanitize",function(){this.$get=["$$sanitizeUri",function(a){return function(f){var d=[];F(f,r(d,function(c,b){return!/^unsafe/.test(a(c,b))}));return d.join("")}}]});h.module("ngSanitize").filter("linky",["$sanitize",function(a){var f=/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,d=/^mailto:/i;return function(c,b){function k(a){a&&g.push(E(a))}function e(a,
c){g.push("<a ");h.isDefined(b)&&g.push('target="',b,'" ');g.push('href="',a.replace(/"/g,"&quot;"),'">');k(c);g.push("</a>")}if(!c)return c;for(var m,l=c,g=[],n,p;m=l.match(f);)n=m[0],m[2]||m[4]||(n=(m[3]?"http://":"mailto:")+n),p=m.index,k(l.substr(0,p)),e(n,m[0].replace(d,"")),l=l.substring(p+m[0].length);k(l);return a(g.join(""))}}])})(window,window.angular);
//# sourceMappingURL=angular-sanitize.min.js.map

View File

@@ -1,6 +1,6 @@
angular.module('satbd.satellite.bar', ['ui.bootstrap','ngAnimate']);
angular.module('satbd.satellite.bar', ['ui.bootstrap','ngAnimate','ngSanitize']);
angular.module('satbd.satellite.bar').controller('GlobalCtrl', function($scope) {
angular.module('satbd.satellite.bar').controller('GlobalCtrl', function($scope,$log) {
$scope.location = '';
$scope.isActive = function(location) {
return $scope.location === location
@@ -21,15 +21,15 @@ angular.module('satbd.satellite.bar').controller('GlobalCtrl', function($scope)
$scope.$broadcast('displayAuthors')
};
$scope.search = function( query ) {
$scope.$on('onSearchQuery', function(event,query) {
$scope.location='search';
$scope.$broadcast('displaySearch', query)
};
});
$scope.$on('recentsReady', function(event) {
$scope.recents();
});
});
angular.module('satbd.satellite.bar').controller('NavbarCollapseCtrl', function ($scope, $uibModal) {
@@ -43,6 +43,11 @@ angular.module('satbd.satellite.bar').controller('NavbarCollapseCtrl', function
});
};
$scope.searchQuery = '';
$scope.search = function() {
$scope.$emit('onSearchQuery',$scope.searchQuery);
};
});
angular.module('satbd.satellite.bar').controller('HelpInstanceCtrl', function($scope, $uibModalInstance) {
@@ -78,3 +83,37 @@ angular.module('satbd.satellite.bar').controller('RecentAlbumCtrl', function($sc
});
};
});
angular.module('satbd.satellite.bar').controller('SearchCtrl', function($scope, $http, $log) {
$scope.showResults = false
$scope.searchResults = null;
$scope.searchResultsJSON = '';
$scope.clear = function () {
$scope.showResults = false;
$scope.searchResults = null;
$scope.searchResultsJSON = '';
};
$scope.$on('displayAuthors', $scope.clear);
$scope.$on('displayCollections', $scope.clear);
$scope.$on('displayRecents', $scope.clear);
$scope.$on('displaySearch', function(event, query) {
$log.info("Submitting query " + query);
$http.post('/api/search', {
"size": 20,
"explain": false,
"highlight":{},
"fields": ["série", "collection","titre","éditeur","ref","description","Note"],
"query": {
"query": query,
}
}).success(function(data) {
$scope.searchResults = data;
$scope.showResults = true;
}).error(function(data, code) {
$log.error("got " + code + " data: " + data);
$scope.showResults = true;
});
});
});