Commit 40d432f3 authored by Nacim Goura's avatar Nacim Goura

add autocomplete, synonym and fix bug

parent 29374670
......@@ -7,7 +7,7 @@
# base package
meteor-base@1.1.0 # Packages every Meteor app needs to have
mobile-experience@1.0.4 # Packages for a great mobile UX
mongo@1.1.19 # The database Meteor supports right now
mongo # The database Meteor supports right now
blaze-html-templates # Compile .html files into Meteor Blaze views
reactive-var@1.0.11 # Reactive variable for tracker
tracker@1.1.3 # Meteor's client-side reactive programming library
......
......@@ -67,10 +67,10 @@ mobile-status-bar@1.0.14
modules@0.9.2
modules-runtime@0.8.0
momentjs:moment@2.18.1
mongo@1.1.19
mongo@1.1.22
mongo-id@1.0.6
npm-bcrypt@0.9.3
npm-mongo@2.2.24
npm-mongo@2.2.30
observe-sequence@1.0.16
ordered-dict@1.0.9
ostrio:cookies@2.2.2
......
......@@ -15,3 +15,4 @@
@import "../imports/ui/stylesheets/izitoast";
@import "../imports/ui/stylesheets/sweetalert2";
@import "../imports/ui/stylesheets/autocomplete";
@import "../imports/ui/stylesheets/resultSearch";
......@@ -5,6 +5,7 @@ import { Accounts } from 'meteor/accounts-base';
import { Roles } from 'meteor/alanning:roles';
import formAccountSchema from '/imports/api/account/formAccountSchema';
import { defineConfig } from '/imports/api/config/methods';
import Search from '/imports/api/search/server/search';
/**
* add user account
......@@ -34,4 +35,15 @@ export function deleteAccount(id) {
Meteor.methods({
addAccount,
deleteAccount,
testCharge: () => {
// pour la test de charge
const search = new Search('l6g4zuw2vsbegqzw7');
return search.searchWebsite('anap')
.then((result) => {
console.log(result);
return JSON.stringify(result);
}).catch((error) => {
console.log(error);
});
},
});
......@@ -147,7 +147,8 @@ export default class crawlWebsite extends CrawlGeneric {
${$('meta[property="og:title"]').attr('content')}
${$('meta[name=title]').attr('content')}
${$('meta[property="video:actor"]').attr('content')}
${$('meta[property="video:director"]').attr('content')} `;
${$('meta[property="video:director"]').attr('content')}
${$('meta[property="og:description"]').attr('content')} `;
const dataForIndex = {
tag: 'website',
jobName: this.name,
......@@ -155,10 +156,11 @@ export default class crawlWebsite extends CrawlGeneric {
title,
title_suggest: {
// replace - and _ and multiple space for autocompletion
input: title.replace(/[-_]/g, ' ').replace(/[^\S]{2,}/, ' ').trim(),
input: title.replace(/[-_]/g, ' ').replace(/[^\S]{2,}/g, ' ').trim(),
},
description: checkData.cleanText(description),
body: checkData.cleanText(body.text()),
image: $('meta[property="og:image"]').attr('content'),
html: body.html(),
urlText: checkData.cleanText(decodeURI(currentUrl)).replace(/http|www|html/g, '').replace(/\.|-/g, ' '),
url: decodeURI(currentUrl),
......
......@@ -7,5 +7,8 @@ export default new SimpleSchema({
searchTerm: {
label: false,
type: String,
autoform: {
type: 'search',
},
},
}, { tracker: Tracker });
......@@ -12,6 +12,7 @@ export async function searchWebsite(data, userId) {
try {
const results = await search.searchWebsite(data.searchTerm);
return {
time: results.took ? results.took / 1000 : null,
total: results.hits.total,
list: _.map(results.hits.hits, '_source'),
};
......
......@@ -27,7 +27,7 @@ export default class Search {
* common ( sépare les tokens les plus présents dans l’index des autres, et ne les utilise que pour améliorer la pertinence )
* fuzziness (permet une recherche même avec des fautes)
*/
params._source = ["title", "url"];
params._source = ["title", "url", "description", "image"];
params.query = {
"bool": {
"must_not": [
......
......@@ -15,6 +15,10 @@ exports.analyser = {
],
},
// synonyme
/**
* Attention après ajout de synonyme, réindexer
* https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-synonym-tokenfilter.html
*/
french_synonym: {
type: 'synonym',
ignore_case: true,
......@@ -25,8 +29,43 @@ exports.analyser = {
'anap, Agence nationale d\'appui à la performance des établissements de santé et médico-sociaux',
'ars, agences régionales de santé',
'c dans l\'oxygene, c dans l\'air',
'+, plus',
'%, pour cent',
'+ => plus',
'% => pour cent',
'10% => 10 pour cent, 10 pourcent,dix pour cent, dix pourcent',
'1 => un',
'2 => deux',
'3 => trois',
'4 => quatre',
'5 => cinq',
'6 => six',
'7 => sept',
'8 => huit',
'9 => neuf',
'10 => dix',
'11 => onze',
'12 => douze',
'13 => treize',
'14 => quatorze',
'15 => quinze',
'16 => seize',
'17 => dix-sept,dix sept',
'18 => dix-huit,dix huit',
'19 => dix-neuf,dix neuf',
'20 => vingt',
'min, minute, minimum',
'boulevard, rue, avenue',
'ville, village',
'cosmos, galaxie,univers',
'docteur, medecin, doctor',
'foot2rue, foot de rue,foot 2 rue',
'animaux, betes',
'chine, asie, asiatique, chinois, cantonais, jaune',
'accusé, coupable',
'sdf, sans domilcile fixe',
'Histoire, légende',
'tgv, train, ter, sncf, train grande vitesse',
'canada,canadienne',
'terrien,terre',
],
},
// radical des mots
......@@ -164,6 +203,10 @@ exports.mapping = {
},
},
},
image: {
type: 'text',
analyzer: 'standard',
},
h1: {
type: 'text',
analyzer: 'french_light',
......
......@@ -2,6 +2,7 @@
import 'bootstrap-sass';
import moment from 'moment';
import 'moment/locale/fr';
import './routes';
moment.locale('fr');
......@@ -8,3 +8,4 @@ import './register-api';
// task of cron
import './cron';
<template name="listResultSearch">
<template name="listResultSearchTpl">
<div class="panel panel-default wrapper">
<div class="panel-body">
......@@ -7,7 +7,11 @@
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading text-center">
<h3 class="panel-title">Résultat de site</h3>
<h3 class="panel-title">Résultats de site
{{#if websiteResults.time}}
<span class="small">(trouvés en {{websiteResults.time}} secondes)</span>
{{/if}}
</h3>
</div>
<div class="panel-body">
{{#each result in websiteResults.list}}
......@@ -24,7 +28,7 @@
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading text-center">
<h3 class="panel-title">Résultat de réseaux sociaux</h3>
<h3 class="panel-title">Résultats de réseaux sociaux</h3>
</div>
<div class="panel-body">
{{#each result in apiResults.list}}
......@@ -38,7 +42,7 @@
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading text-center">
<h3 class="panel-title">Résultat de fichiers</h3>
<h3 class="panel-title">Résultats de fichiers</h3>
</div>
<div class="panel-body">
{{#each result in documentResults.list}}
......
import { Template } from 'meteor/templating';
import './list.html';
Template.listResultSearch.helpers({
Template.listResultSearchTpl.helpers({
websiteResults: () => Session.get('websiteResults'),
apiResults: () => Session.get('apiResults'),
documentResults: () => Session.get('documentResults'),
......
......@@ -5,6 +5,7 @@ import TabsCollection from '/imports/api/tabs/tabsCollection';
import '/imports/ui/components/tabs/tabs';
import '/imports/ui/components/resultSearch/list/list';
import '/imports/ui/components/resultSearch/vignette/vignette';
import './resultSearch.html';
......
<template name="vignetteResultSearchTpl">
<div class="panel panel-default wrapper">
<div class="panel-body">
{{#if websiteResults}}
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading text-center">
<h3 class="panel-title">Résultats de site
{{#if websiteResults.time}}
<span class="small">(trouvés en {{websiteResults.time}} secondes)</span>
{{/if}}
</h3>
</div>
<div class="panel-body">
{{#each result in websiteResults.list}}
<div class="well search-result">
<div class="row">
<a href="{{result.url}}">
<div class="col-xs-6 col-sm-3 col-md-3 col-lg-2">
{{#if result.image}}
<img class="img-responsive" src="{{result.image}}" alt="{{result.title}}">
{{/if}}
</div>
<div class="col-xs-6 col-sm-9 col-md-9 col-lg-10 title">
<h3>{{result.title}}</h3>
<span class="small">{{result.url}}</span>
<p>{{truncate result.description}}</p>
</div>
</a>
</div>
</div>
{{/each}}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading text-center">
<h3 class="panel-title">Résultats de réseaux sociaux</h3>
</div>
<div class="panel-body">
{{#each result in apiResults.list}}
<li>
<a href="{{result.url}}" target="_blank">{{result.title}}</a>
</li>
{{/each}}
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading text-center">
<h3 class="panel-title">Résultats de fichiers</h3>
</div>
<div class="panel-body">
{{#each result in documentResults.list}}
<li>
{{result.attachment.title}}
</li>
{{/each}}
</div>
</div>
</div>
</div>
{{else}}
<h3 class="text-center">Aucun résultat!</h3>
{{/if}}
</div>
</div>
</template>
import { Template } from 'meteor/templating';
import truncate from 'lodash/truncate';
import './vignette.html';
Template.vignetteResultSearchTpl.helpers({
websiteResults: () => Session.get('websiteResults'),
apiResults: () => Session.get('apiResults'),
documentResults: () => Session.get('documentResults'),
truncate: term => truncate(term, {
length: 100,
}),
});
......@@ -5,14 +5,14 @@
<div class="autocomplete-holder">
{{#autoForm id="formSearch" schema=formSearchSchema resetOnSuccess=false method="post" type="method" meteormethod="searchAll" }}
<div class="input-group">
{{> afQuickField name='searchTerm' placeholder="Recherche..."}}
{{> afQuickField name='searchTerm' class="form-control awesomplete" placeholder="Recherche..." }}
<span class="input-group-btn">
<button type="submit" class="btn btn-primary">
<i class="fa fa-search"></i>
</button>
</span>
</div>
{{#if listAutoCompleteResults}}
<!--{{#if listAutoCompleteResults}}
<div class="autocomplete-dropdown">
{{#each result in listAutoCompleteResults}}
<div class="autocomplete-row">
......@@ -20,7 +20,7 @@
</div>
{{/each}}
</div>
{{/if}}
{{/if}}-->
{{/autoForm}}
</div>
......
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { Session } from 'meteor/session';
import formSearchSchema from '/imports/api/search/formSearchSchema';
import '/imports/api/search/formSearchHooks';
import Awesomplete from 'awesomplete';
import 'awesomplete/awesomplete.css';
import '/imports/ui/components/resultSearch/resultSearch';
import './search.html';
let awesomplete = null;
Template.searchTpl.hooks({
rendered() {
const id = $('input[name$=searchTerm]').attr('id');
awesomplete = new Awesomplete(document.querySelector(`#${id}`), {
filter(text, input) {
return text;
},
});
},
});
Template.searchTpl.helpers({
listAutoCompleteResults: () => Session.get('autoCompleteResults'),
formSearchSchema,
......@@ -15,18 +32,15 @@ Template.searchTpl.helpers({
Template.searchTpl.events({
'input input[name$=searchTerm]': (event) => {
event.preventDefault();
const term = event.target.value;
if (term && term.length > 2 && term.length < 20) {
if (term && term.length >= 2 && term.length < 20) {
Meteor.callPromise('autoCompletion', term)
.then((results) => {
Session.set('autoCompleteResults', results);
awesomplete.list = results;
});
}
},
'click .autocomplete-title': (event) => {
event.preventDefault();
......
......@@ -14,13 +14,14 @@
<hr>
<!--
<div class="row">
<label>Liste des synonymes : </label>
<textarea class="form-control" name="synonym" id="" cols="30" rows="10"></textarea>
<div class="alert alert-warning">
<strong>Attention!</strong> Changer les synonymes nécessite une réindexation!
</div>
</div>
</div>-->
</div>
</div>
</template>
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import $ from 'jquery';
import './demo.html';
Template.demoTpl.hooks({
rendered() {
console.log('rendu');
Meteor.callPromise('testCharge')
.then((result) => {
$('html').html(result);
});
},
});
......@@ -18,3 +18,14 @@
background-color: #abdde6;
color: #fff;
}
.awesomplete {
display: block !important;
ul {
margin: 3em 0 0 !important;
}
}
.input-group-btn {
padding-top: 21px;
}
body{
background:#eee;
}
.search-result .title h3 {
margin: 0 0 15px;
font-size: 20px;
color: #333;
}
.search-result .title p {
font-size: 12px;
color: #333;
}
.well {
border: 0;
padding: 20px;
min-height: 63px;
background: #fff;
box-shadow: none;
border-radius: 3px;
position: relative;
max-height: 100000px;
border-bottom: 2px solid #ccc;
transition: max-height 0.5s ease;
-o-transition: max-height 0.5s ease;
-ms-transition: max-height 0.5s ease;
-moz-transition: max-height 0.5s ease;
-webkit-transition: max-height 0.5s ease;
}
.form-control {
height: 45px;
padding: 10px;
font-size: 16px;
box-shadow: none;
border-radius: 0;
position: relative;
}
......@@ -44,6 +44,8 @@ const checkData = {
resultText = resultText.replace(/([^A-Z'`])([A-Z])/g, '$1 $2');
// remplace les caractères spéciaux
resultText = resultText.replace(/[\/<>_():\\«»"]/g, ' ');
// enleve undefined ou null
resultText = resultText.replace(/undefined|null/g, ' ');
// remplace les espaces et les retours à la ligne par un espace
resultText = resultText.replace(/(\s{2,})/g, ' ');
return unfancy(resultText);
......@@ -62,9 +64,6 @@ const checkData = {
allowedSchemes: ['http', 'https', 'ftp', 'mailto'],
allowedSchemesByTag: {},
allowProtocolRelative: true,
transformTags: {
li: ' ',
},
});
},
......
......@@ -8,12 +8,13 @@
"eslint": "eslint .; exit 0"
},
"dependencies": {
"awesomplete": "^1.1.2",
"babel-runtime": "^6.23.0",
"bcrypt": "^1.0.2",
"bootstrap-sass": "^3.3.7",
"crawler": "^1.0.5",
"datatables.net-bs": "^1.10.15",
"detergent": "^2.28.3",
"detergent": "^2.30.0",
"elasticsearch": "^13.2.0",
"izitoast": "^1.1.4",
"jquery": "^1.11.2",
......
......@@ -76,12 +76,21 @@
"resultSearch": [
{
"module": "resultSearch",
"layout": "listResultSearch",
"layout": "listResultSearchTpl",
"label": "Liste",
"state": {},
"activ": true,
"closable": false,
"sort": 1
},
{
"module": "resultSearch",
"layout": "vignetteResultSearchTpl",
"label": "Vignette",
"state": {},
"activ": false,
"closable": false,
"sort": 2
}
]
}
......
......@@ -153,6 +153,10 @@ asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
awesomplete@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/awesomplete/-/awesomplete-1.1.2.tgz#b6e253f73474e46278bba5ae7f81d4262160fb75"
aws-sign2@~0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
......@@ -853,18 +857,16 @@ detect-indent@^4.0.0:
dependencies:
repeating "^2.0.0"
detergent@^2.28.3:
version "2.28.3"
resolved "https://registry.yarnpkg.com/detergent/-/detergent-2.28.3.tgz#e85bbcae064e57d1e998a49a3e0888275969cade"
detergent@^2.30.0:
version "2.30.0"
resolved "https://registry.yarnpkg.com/detergent/-/detergent-2.30.0.tgz#faa5fbac2e2065d5c06712137adfb9ab66f5c9ad"
dependencies:
curl-quotes "^0.1.0"
easy-replace "^2.10.0"
he "^1.0.0"
lodash.clonedeep "^4.5.0"
lodash.isplainobject "^4.0.6"
lodash.toarray "^4.4.0"
lodash.clonedeep "*"
lodash.isplainobject "*"
lower-case "^1.1.4"
object-assign "^4.1.1"
string "^3.3.1"
typographic-en-dashes "^1.0.1"
unicode-dragon "^0.1.3"
......@@ -1854,7 +1856,7 @@ lodash.bind@^4.1.4:
version "4.2.1"
resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35"
lodash.clonedeep@*, lodash.clonedeep@^4.5.0:
lodash.clonedeep@*:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
......@@ -1898,7 +1900,7 @@ lodash.isempty@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e"
lodash.isplainobject@*, lodash.isplainobject@^4.0.6:
lodash.isplainobject@*:
version "4.0.6"
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
......@@ -1934,7 +1936,7 @@ lodash.some@^4.4.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
lodash.toarray@*, lodash.toarray@^4.4.0:
lodash.toarray@*:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
......@@ -2156,7 +2158,7 @@ oauth-sign@~0.8.1:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
object-assign@*, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
object-assign@*, object-assign@^4.0.1, object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment