Changing search method in Prestashop - search

The search function is as shown below matching results after 3 characters and input and they match the product name or description. I'm looking for a change in search function of MegaShop theme in Prestashop 1.7 as follows:
The search should be able to find words parts. In example, If the user writes "hi he", the search should be able to find "high heels". This should work also in other orders, lets say "he hi" (instead of "hi he") would return also "high heels" and every other article that match these word parts in different words.
Inside /root/modules/tptnsearch the file "tptnsearch-ajax-php contains:
<?php
require_once('../../config/config.inc.php');
require_once('../../init.php');
require_once(dirname(__FILE__).'/tptnsearch.php');
$tptnsearch = new TptnSearch();
$result_products = array();
$products = array();
$tptnsearch_key = Tools::getValue('search_key');
$context = Context::getContext();
$count = 0;
$product_link = $context->link;
if (Tools::strlen($tptnsearch_key) >= 3) {
$products = Product::searchByName($context->language->id, $tptnsearch_key);
$total_products = count($products);
if ($total_products) {
for ($i = 0; $i < $total_products; $i++) {
if (($products[$i]['name']) && ($products[$i]['active'])) {
$images = Image::getImages($context->language->id, $products[$i]['id_product']);
$product = new Product($products[$i]['id_product']);
$products[$i]['link'] = $product_link->getProductLink($products[$i]['id_product'], $product->link_rewrite[1], $product->id_category_default, $product->ean13);
$products[$i]['link_rewrite'] = $product->link_rewrite[1];
$products[$i]['id_image'] = $images[0]['id_image'];
$products[$i]['price'] = Tools::displayPrice(Tools::convertPrice($products[$i]['price_tax_incl'], $context->currency), $context->currency);
if ($count < Configuration::get('TPTN_SEARCH_COUNT')) {
$result_products[] = $products[$i];
$count ++;
} else {
break;
}
}
}
}
$context->smarty->assign(array(
'enable_image' => Configuration::get('TPTN_SEARCH_IMAGE'),
'enable_price' => Configuration::get('TPTN_SEARCH_PRICE'),
'enable_name' => Configuration::get('TPTN_SEARCH_NAME'),
'search_alert' => $tptnsearch->no_product,
'link' => $context->link,
'products' => $result_products,
));
$context->smarty->display(dirname(__FILE__).'/views/templates/hook/popupsearch.tpl');
} else {
echo '<div class="wrap_item">'.$tptnsearch->three_character.'</div>';
}
I believe changes must be done within this file.

I think your approach wouldn't give you desirable behavior. Basically, I think you need to create your own search query or override existed one and modify SQL query. Because now there are only LIKE %text% conditions and mean that your text should appear in the exact same way. So it means that that you are able to find "gh he" but not "hi he".
Or you can split your search request by gaps and then search by words doing the checking if all of them are in the request. But also I think it would be better to modify LIKE from %text% to %text to exclude duplicating and search only by words beginnings

Related

How do I train Bixby to recognize a wild card search term?

I have an action FindPage.js that finds pages and retrieves them for display as results. I understand how to train it to find pages with utterances like "Read the Twitter Search page" or "Read the Searchable Text page". The training treats "Twitter Search" as SearchTerm and the code below matches SearchTerm to the tag field in the data. But how would I train to understand a command like "Read all pages"? I want the code to carry out a search on the wildcard and bring back all available pages.
// search for informational pages
var console = require('console');
const PAGES = require('./content/pages')
pages = PAGES
console.log('pages are', pages)
exports.function = function findPage (searchTerm) {
console.log('searchTerm is', searchTerm)
var matches = []
pages = PAGES
for (var i = 0; i < pages.length; i++) {
if (searchTerm == pages[i].tag) {
matches.push(pages[i])
}
else
{ console.log('no tag matches')
}
}
console.log('matches are', matches)
return matches
}
Training:
[g:Page] Read the (Twitter Search)[v:SearchTerm] page.
This works although I feel it is somewhat clunky to hardcode a conversion from "all" to the include wildcard string, which is ''.
exports.function = function findPage (searchTerm) {
//console.log('searchTerm is', searchTerm)
if (searchTerm == 'all') {
searchTerm = ''
console.log('searchTerm is all', searchTerm)
}
else
{ console.log('searchTerm is not all', searchTerm)
}
var matches = []
pages = PAGES
matches = pages.filter(function(pages) {
return pages.tag.includes(searchTerm);
});
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes#Examples See example
const str = 'To be, or not to be, that is the question.';
console.log(str.includes('To be')); // true
console.log(str.includes('')) // true

Search shows nothing if user types category name in search bar in osclass

I hope this will help others too.
The search bar shows nothing if a user types category/sub-category name in the search bar.
I know there is a separate field for selecting category/sub-category along with the search bar but in my case i have only a single search bar and when i search by typing the category/sub-category name the search shows me 0 results found even though i have items in the category/sub-category.
Is there anyone could assist me the solution please
The search in osclass is made by title and description only. If you insert the category name into the description then it will work as you want.
In your theme's functions.php file, add to the end
<?php
function endsWith($haystack, $needle)
{
$length = strlen($needle);
return $length === 0 ||
(substr($haystack, -$length) === $needle);
}
function mc_addcategory($desc, $catId) {
$Cat = Category::newInstance()->toRootTree($catId);
$d = '\n\n ';
foreach($Cat as $c) {
$d = $d . $c["s_name"] .' / ';
}
if(endsWith($desc, $d))
return $desc;
else return $desc . $d;
}
function mc_filter_description($aItem) {
foreach(#$aItem['description'] as $key => $value) {
$aItem['description'][$key] = mc_addcategory($value,$aItem['catId']);
}
return $aItem;
}
osc_add_filter('item_add_prepare_data', 'mc_filter_description');
osc_add_filter('item_edit_prepare_data', 'mc_filter_description');
?>
Step 2
You would want to remove the added text from description when displaying the description to the public and to the owner when it edits hi's item.
You can do that with a function
Put this before the above code in functions.php
<?php
function mc_hide_categ_in_description($desc, $catId) {
$Cat = Category::newInstance()->toRootTree($catId);
$d = '\n\n ';
foreach($Cat as $c) {
$d = $d . $c["s_name"] .' / ';
}
if(endsWith($desc, $d))
return str_replace($d,'',$desc);
else return $desc;
}
?>
and call this function where the description is shown. Have a look here
How to modify ft_min_word_len=4 to ft_min_word_len=1 so that osclass 3.7.1 can search min 1 character word, instead of 4?
And see where I call 'removeunderline(' for description.
OBS
After this you must update the descriptions in your db by editing and saving each item.
This will work if the item is edited and it's category is changed by user.
If the admin makes the editing and the above function can't be called from oc-admin/themes/modern... files, the last function from above must be placed in /oc-includes/osclass/helpers/hSearch.php and removed from functions.php file.

PHP + recursive menu tree and one path

Greeting,
am working on a recursive tree menu, but am getting stuck on somthing.
Recursive code
$get_parent = "0";
$rep_1 = mysqli_query($connexion,' SELECT * FROM category');
$get_array = array();
while($don_1 = mysqli_fetch_array($rep_1))
{
$get_array[$don_1['id']] = array("id" => $don_1['id'], "id_parent" => $don_1['id_parent'], "title" => $don_1['title']);
}
function tree_2($array,$parent,$currLevel=0)
{
foreach($array as $key => $value)
{
if($value['id_parent'] == $parent)
{
echo "".str_repeat("-", $currLevel)."id : ".$value['id']." | id_parent : ".$value['id_parent']." | title : ".$value['title']."<br/>";
$currLevel++;
$children = tree_2($array,$key,$currLevel);
$currLevel--;
}
}
}
echo tree_2($get_array,$get_parent);
My table
== Table structure for table category
|------
|Column|Type|Null|Default
|------
|//**id**//|int(11)|No|
|title|varchar(20)|No|
|id_parent|int(11)|Yes|NULL
|path|varchar(11)|No|
== Dumping data for table category
|1|ELECTRONICS|0|0
|2|TELEVISIONS|1|1
|3|TUBE|2|2
|4|LCD|2|2
|5|PLASMA|2|2
|6|PORTABLE ELECTRONICS|1|1
|7|MP3 PLAYERS|6|2
|8|FLASH|7|2
|9|CD PLAYERS|6|2
|10|2 WAY RADIOS|6|3
|11|PLANS|0|0
|12|SPITFIRE|11|1
|13|MP3 PLAYERS|8|2
This code work's very well,
however am stuck for when i want to get only one branch. Like
FLASH
CD PLAYERS
WAY RADIO.
In fact, i'd like to do like into forums, where we can get the path back to the forum root. Like
FLASH > CD PLAYERS > WAY RADIO.
If any idea !
after some try, I finally found a solution to get from a php recursive tree only one branch. am searing the code, for those who are looking for this kind of script.
Sample source come from this blog : Managing Hierarchical Data in MySQL
The table source :
===Database model3
== Table structure for table category
|------
|Column|Type|Null|Default
|------
|//**id**//|int(11)|No|
|title|varchar(20)|No|
|id_parent|int(11)|Yes|NULL
|path|varchar(11)|No|
== Dumping data for table category
|1|ELECTRONICS|0|0
|2|TELEVISIONS|1|1/2
|3|TUBE|2|1/2/3
|4|LCD|2|1/2/4
|5|PLASMA|2|1/2/5
|6|PORTABLE ELECTRONICS|1|1/6
|7|MP3 PLAYERS|6|1/6/7
|8|FLASH|7|1/6/7/8
|9|CD PLAYERS|6|1/6/9
|10|2 WAY RADIOS|6|1/6/10
|11|PLANS|0|0
|12|SPITFIRE|11|11/12
|13|LOW QUALITY|8|1/6/7/8/13
Before the code, some explanation :
With php recursive tree, we are able to get from an array a tree with its branch, like this :
ELECTRONICS
TELEVISIONS
TUBE
LCD
PLASMA
PORTABLE ELECTRONICS
MP3 PLAYERS
FLASH
LOW QUALITY
CD PLAYERS
2 WAY RADIOS
PLANS
SPITFIRE
The very simple code that I use for the sample over :
$get_parent = "0";
$rep_1 = mysqli_query($connexion,' SELECT * FROM category');
$get_array = array();
while($don_1 = mysqli_fetch_array($rep_1))
{
$get_array[$don_1['id']] = array("id" => $don_1['id'], "id_parent" => $don_1['id_parent'], "title" => $don_1['title']);
}
mysqli_free_result($rep_1);
function tree($array,$parent,$currLevel=0)
{
foreach($array as $key => $value)
{
if($value['id_parent'] == $parent)
{
echo "".str_repeat("-", $currLevel)."title : ".$value['title']."<br/>";
$currLevel++;
tree($array,$key,$currLevel);
$currLevel--;
}
}
}
echo tree($get_array,$get_parent);
The The Adjacency List Model is great for simple mysql query, however, after several days in google search, I found that it was a real head hash to Retrieving a Single Path as we must know the final level, for static menu its fine, but for other purpose like forum parent or pages parent I found it wasn't the best way to process for Retrieving a Single Path.
I documented about the The Nested Set Model, on paper, it's great. But I found it was a bit a mess when INSERT / UPDATE and DELETE are requested.
I finally did some test with the path enumeration : Hierarchical data in MySQL (and other RDBMS) and found a solution for Retrieving a Single Path, like this :
ELECTRONICS
PORTABLE ELECTRONICS
FLASH
LOW QUALITY
The very simple code that am using :
$get_parent = "0";
$rep_1 = mysqli_query($connexion,' SELECT * FROM category WHERE id=7');
$get_array = array();
while($don_1 = mysqli_fetch_array($rep_1))
{
$explod_array = explode("/",$don_1['path'],9999);
foreach ($explod_array as $key=>$value)
{
$rep_2 = mysqli_query($connexion,' SELECT * FROM category WHERE id="'.$value.'"');
while($don_2 = mysqli_fetch_array($rep_2))
{
$get_array[$don_2['id']] = array("id" => $don_2['id'], "id_parent" => $don_2['id_parent'], "title" => $don_2['title']);
}
mysqli_free_result($rep_2);
}
}
mysqli_free_result($rep_1);
function tree_1($array,$parent,$currLevel=0)
{
foreach($array as $key => $value)
{
if($value['id_parent'] == $parent)
{
echo "".str_repeat("-", $currLevel)."id : ".$value['id']." | id_parent : ".$value['id_parent']." | title : ".$value['title']."<br/>";
$currLevel++;
tree_1($array,$key,$currLevel);
$currLevel--;
}
}
}
echo tree_1($get_array,$get_parent);
This way, I keep the same php recursive tree menu code. I do not charge my mysql table with a big query.
The badest point, is that I will have to code a bit more for the INSERT / UPDATE AND DELETE query, but I already worked on it, and it's doable with a little code.
I hope it will help.

Distinct values in Azure Search Suggestions?

I am offloading my search feature on a relational database to Azure Search. My Products tables contains columns like serialNumber, PartNumber etc.. (there can be multiple serialNumbers with the same partNumber).
I want to create a suggestor that can autocomplete partNumbers. But in my scenario I am getting a lot of duplicates in the suggestions because the partNumber match was found in multiple entries.
How can I solve this problem ?
The Suggest API suggests documents, not queries. If you repeat the partNumber information for each serialNumber in your index and then suggest based on partNumber, you will get a result for each matching document. You can see this more clearly by including the key field in the $select parameter. Azure Search will eliminate duplicates within the same document, but not across documents. You will have to do that on the client side, or build a secondary index of partNumbers just for suggestions.
See this forum thread for a more in-depth discussion.
Also, feel free to vote on this UserVoice item to help us prioritize improvements to Suggestions.
I'm facing this problem myself. My solution does not involve a new index (this will only get messy and cost us money).
My take on this is a while-loop adding 'UserIdentity' (in your case, 'partNumber') to a filter, and re-search until my take/top-limit is met or no more suggestions exists:
public async Task<List<MachineSuggestionDTO>> SuggestMachineUser(string searchText, int take, string[] searchFields)
{
var indexClientMachine = _searchServiceClient.Indexes.GetClient(INDEX_MACHINE);
var suggestions = new List<MachineSuggestionDTO>();
var sp = new SuggestParameters
{
UseFuzzyMatching = true,
Top = 100 // Get maximum result for a chance to reduce search calls.
};
// Add searchfields if set
if (searchFields != null && searchFields.Count() != 0)
{
sp.SearchFields = searchFields;
}
// Loop until you get the desired ammount of suggestions, or if under desired ammount, the maximum.
while (suggestions.Count < take)
{
if (!await DistinctSuggestMachineUser(searchText, take, searchFields, suggestions, indexClientMachine, sp))
{
// If no more suggestions is found, we break the while-loop
break;
}
}
// Since the list might me bigger then the take, we return a narrowed list
return suggestions.Take(take).ToList();
}
private async Task<bool> DistinctSuggestMachineUser(string searchText, int take, string[] searchFields, List<MachineSuggestionDTO> suggestions, ISearchIndexClient indexClientMachine, SuggestParameters sp)
{
var response = await indexClientMachine.Documents.SuggestAsync<MachineSearchDocument>(searchText, SUGGESTION_MACHINE, sp);
if(response.Results.Count > 0){
// Fix filter if search is triggered once more
if (!string.IsNullOrEmpty(sp.Filter))
{
sp.Filter += " and ";
}
foreach (var result in response.Results.DistinctBy(r => new { r.Document.UserIdentity, r.Document.UserName, r.Document.UserCode}).Take(take))
{
var d = result.Document;
suggestions.Add(new MachineSuggestionDTO { Id = d.UserIdentity, Namn = d.UserNamn, Hkod = d.UserHkod, Intnr = d.UserIntnr });
// Add found UserIdentity to filter
sp.Filter += $"UserIdentity ne '{d.UserIdentity}' and ";
}
// Remove end of filter if it is run once more
if (sp.Filter.EndsWith(" and "))
{
sp.Filter = sp.Filter.Substring(0, sp.Filter.LastIndexOf(" and ", StringComparison.Ordinal));
}
}
// Returns false if no more suggestions is found
return response.Results.Count > 0;
}
public async Task<List<string>> SuggestionsAsync(bool highlights, bool fuzzy, string term)
{
SuggestParameters sp = new SuggestParameters()
{
UseFuzzyMatching = fuzzy,
Top = 100
};
if (highlights)
{
sp.HighlightPreTag = "<em>";
sp.HighlightPostTag = "</em>";
}
var suggestResult = await searchConfig.IndexClient.Documents.SuggestAsync(term, "mysuggestion", sp);
// Convert the suggest query results to a list that can be displayed in the client.
return suggestResult.Results.Select(x => x.Text).Distinct().Take(10).ToList();
}
After getting top 100 and using distinct it works for me.
You can use the Autocomplete API for that where does the grouping by default. However, if you need more fields together with the result, like, the partNo plus description it doesn't support it. The partNo will be distinct though.

Drupal views: List of authors of nodes where node.type=abc and node.vocabulary_id=123

In drupal6 using views I want a (block) list of authors (with complete profile fields) of some specific node type AND taxonomy term.id OR vocabulary.id
Summarized query:
Views: type user
Argument: Term ID/Vocabulary ID
Filters: Author of Node type abc
Fields: All Profile/Content Profile Fields
How can I achieve such solution?
I have the same issue. I found that if I filtered by node.type = 'blog' and set fields for the profile fields I was interested in, I could get a list or authors, but there would be duplicates. Setting 'Distinct' to Yes didn't help because it was selecting out distinct nodes, not distinct users.
So I ended up creating a custom block to show this information with some code like this:
<?php
$block['subject'] = t('Bloggers');
// Get a list of blog authors
$result = db_query('SELECT DISTINCT u.uid, u.name FROM {node} n INNER JOIN {users} u ON n.uid = u.uid WHERE n.type = \'blog\'');
$links = array();
while ($blogger = db_fetch_object($result)) {
$link = array();
if (module_exists('profile')) {
profile_load_profile($blogger);
}
if (!empty($blogger->profile_first_name) || !empty($blogger->profile_last_name)) {
$link['title'] = $blogger->profile_first_name . (empty($blogger->profile_first_name) ? '' : ' ') . $blogger->profile_last_name;
}
else {
$link['title'] = $blogger->name;
}
$link['href'] = 'blog/' . $blogger->uid;
$links[] = $link;
}
$block['content'] = theme('links', $links, array('class' => 'flat-links'));
?>
Hope that helps.

Resources