Unverified Commit aa2ce6bc authored by Sebastian Castro's avatar Sebastian Castro
Browse files

feature: search on multiple fields

parent bc165abe
......@@ -6,6 +6,7 @@ v3.0.0
* FEATURE: Improve SAAS management. Get more info about each map, warn abandonned projects
* BUG: Fix element export
* BUG: Fix hard deleting elements
* FEATURE: Search on multi fields. Can be configured from the Form Builder
v2.5.8
======
......
......@@ -127,3 +127,8 @@ db.Element.find({'data.image': {$exists: true}}).forEach(function(e) {
db.Element.updateMany({'nonDuplicates.$id': "KBL"}, { $unset: { 'nonDuplicates': ''} })
db.Element.dropIndex('name_text')
db.Element.dropIndex('search_index')
db.Element.createIndex( { name: "text", "data.description": "text" }, { name: "search_index", default_language: "french", weights: { name: 10, "data.description": 5 }, })
......@@ -55,7 +55,8 @@ class MigrationCommand extends GoGoAbstractCommand
"Il est maintenant possible de <b>téléverser des images et des fichiers</b> depuis le formulaire d'ajout d'un élément ! Paramétrez ces nouveaux champs dans Modèle de Données -> Formulaire",
// v3.0
"Vous pouvez maintenant écrire des actualités qui seront incluses dans la newsletter automatique! Allez dans Mails/Newsletter -> Actualités",
"L'export des éléments depuis la page Données -> Elements fonctionne de nouveau et inclus cette fois correctement tous les champs personnalisés (y compris fichiers et images)"
"L'export des éléments depuis la page Données -> Elements fonctionne de nouveau et inclus cette fois correctement tous les champs personnalisés (y compris fichiers et images)",
"Depuis le site, la recherche par élément peut maintenant fonctionenr sur plusieurs champs. Dans Modèle de Données -> Formulaire, editez un champ pour voir apparaitre la configuration liée à la recherche. Vous pouvez aussi donner des poids différents à chaque champs, par exemple la recherche sur le titre avec un poids de 3 et la recherche dans la description avec un poids de 1"
];
public function __construct(DocumentManager $dm, LoggerInterface $commandsLogger,
......
......@@ -3,8 +3,10 @@
namespace App\EventListener;
use App\Document\Configuration\ConfigurationApi;
use App\Document\Configuration;
use App\Document\Configuration\ConfigurationMarker;
use App\Services\AsyncService;
use Symfony\Component\Process\Process;
class ConfigurationListener
{
......@@ -34,6 +36,7 @@ class ConfigurationListener
$removedProps = array_diff($oldPrivateProperties, $newPrivateProperties);
$addedProps = array_diff($newPrivateProperties, $oldPrivateProperties);
// Update field path (move them between data and privateData object)
$qb = $dm->createQueryBuilder('App\Document\Element');
$qb = $qb->updateMany();
foreach ($removedProps as $key => $prop) {
......@@ -42,10 +45,16 @@ class ConfigurationListener
foreach ($addedProps as $key => $prop) {
$qb = $qb->field('data.'.$prop)->rename('privateData.'.$prop);
}
$qb->getQuery()->execute();
$this->asyncService->callCommand('app:elements:updateJson', ['ids' => 'all']);
// Update search index
$fullConfig = $dm->getRepository('App\Document\Configuration')->findConfiguration();
$this->updateSearchIndex($fullConfig->getDbName(),
$oldPrivateProperties,
$newPrivateProperties,
$fullConfig->getElementFormFieldsJson(),
$fullConfig->getElementFormFieldsJson());
}
}
if ($document instanceof ConfigurationMarker) {
......@@ -56,5 +65,57 @@ class ConfigurationListener
$this->asyncService->callCommand('app:elements:updateJson', ['ids' => 'all']);
}
}
if ($document instanceof Configuration) {
$uow = $dm->getUnitOfWork();
$uow->computeChangeSets();
$changeset = $uow->getDocumentChangeSet($document);
if (array_key_exists('elementFormFieldsJson', $changeset)) {
$formFieldsChanged = $changeset['elementFormFieldsJson'];
$oldFormFields = $formFieldsChanged[0];
$newFormFields = $formFieldsChanged[1];
$this->updateSearchIndex($document->getDbName(),
$document->getApi()->getPublicApiPrivateProperties(),
$document->getApi()->getPublicApiPrivateProperties(),
$oldFormFields,
$newFormFields);
}
}
}
private function updateSearchIndex($db, $oldPrivateProperties, $newPrivateProperties,
$oldFormFields, $newFormFields) {
$oldSearchIndex = $this->calculateSearchIndexConfig($oldPrivateProperties, $oldFormFields);
$newSearchIndex = $this->calculateSearchIndexConfig($newPrivateProperties, $newFormFields);
if ($oldSearchIndex != $newSearchIndex) {
$command = 'db.Element.dropIndex("name_text");'; // Default index created by doctrine
$command .= 'db.Element.dropIndex("search_index");';
$command .= "db.Element.createIndex( {$newSearchIndex["fields"]}, { name: \"search_index\", default_language: \"french\", weights: {$newSearchIndex["weights"]} });";
$process = new Process("mongo {$db} --eval '{$command}'");
$process->run();
}
}
private function calculateSearchIndexConfig($privateProps, $formFieldsJson) {
$indexConf = [];
$indexWeight = [];
$formFields = json_decode($formFieldsJson);
foreach ($formFields as $key => $field) {
if (property_exists($field, 'search') && $field->search) {
$path = in_array($field->name, $privateProps) ? 'privateData' : 'data';
$path .= '.' . $field->name;
if ($field->name == 'name') $path = 'name';
$indexConf[$path] = "text";
$indexWeight[$path] = (int) $field->searchWeight;
}
}
// default index on name
if (count($indexConf) == 0) {
$indexConf = ['name' => 'text'];
$indexWeight = ['name' => 1];
}
return ['fields' => json_encode($indexConf), 'weights' => json_encode($indexWeight)];
}
}
......@@ -38,8 +38,8 @@ class ElementRepository extends DocumentRepository
}
$qb->limit($maxResults)
->field('status')->gt($status)
->field('geo')->withinCenter((float) $element->getGeo()->getLatitude(), (float) $element->getGeo()->getLongitude(), $radius);
->field('status')->gt($status)
->field('geo')->withinCenter((float) $element->getGeo()->getLatitude(), (float) $element->getGeo()->getLongitude(), $radius);
if ($element->getId()) {
$qb->field('id')->notIn($element->getNonDuplicatesIds());
......@@ -240,13 +240,7 @@ class ElementRepository extends DocumentRepository
if ($config->getSearchExcludingWords()) {
$text = $text.' --'.str_replace(',', ' --', $config->getSearchExcludingWords());
}
// remove words smaller than two letters
$filtered_words = array_filter(explode(' ', $text), function ($el) {
return strlen($el) > 2;
});
$text = implode($filtered_words, ' ');
// return $qb->field('name')->equals(new \MongoRegex("/$text/i"));
return $qb->text($text); //->language('fr');
return $qb->text($text);
}
private function filterVisibles($qb, $status = ElementStatus::PendingModification)
......
......@@ -40,6 +40,8 @@
var iconAttr = { label: 'Icone', placeholder: 'Choisissez une icone' }
var errorMsgAttrs = { label: "Msg. Erreur", placeholder: "Oups ce texte est un peu long ! // Veuillez renseigner une adresse email valide // ..." }
var searchAttrs = { label: 'Recherche dans ce champ', type: 'checkbox' };
var searchWeightAttrs = { label: 'Poids de la recherche', type: 'number', value: "1" };
var typeUserAttrs = {
text: {
icon: iconAttr,
......@@ -51,6 +53,8 @@
'url': 'Url'
},
},
search: searchAttrs,
searchWeight: searchWeightAttrs,
errorMsg: errorMsgAttrs
},
textarea: {
......@@ -60,6 +64,8 @@
'wysiwyg': 'Editeur enrichi',
},
},
search: searchAttrs,
searchWeight: searchWeightAttrs,
errorMsg: errorMsgAttrs,
separator: { label: '' }, // separate important attrs from others
},
......@@ -68,6 +74,8 @@
title: {
maxlength: { label: "Longueur Max."},
icon: iconAttr,
search: searchAttrs,
searchWeight: searchWeightAttrs,
errorMsg: errorMsgAttrs,
separator: { label: '' }, // separate important attrs from others
},
......@@ -123,7 +131,13 @@
});
$(document).ready(function() {
setTimeout(function() { $('.form-field:not(.paragraph-field) .fld-label').each(function() { $(this).text($(this).html()) }); }, 0);
setTimeout(function() {
$('.form-field:not(.paragraph-field) .fld-label').each(function() {
$(this).text($(this).html())
});
$('input[type="checkbox"][value="true"]').prop('checked', true);
}, 0);
});
setInterval(function() {
......@@ -231,4 +245,10 @@
}
.iconpicker-popover.popover.right { right: -200px; left: initial !important;}
.fld-search[type="checkbox"]:after {
content: attr(title);
position: absolute;
padding-left: 2rem;
}
</style>
\ No newline at end of file
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