Javascript-форум (https://javascript.ru/forum/)
-   Элементы интерфейса (https://javascript.ru/forum/dom-window/)
-   -   Помогите подправить поиск на сайте (https://javascript.ru/forum/dom-window/76592-pomogite-podpravit-poisk-na-sajjte.html)

Alexko64 24.01.2019 21:30

Помогите подправить поиск на сайте
 
Есть у меня скрипт для сортировки и поиска элементов на странице.
Поиск учитывает последовательность слов в запросе. Тоисть если у меня элемент называется Samsung Galaxy и я делаю поиск по запросу Galaxy Samsung то ничего не находит, а запрос Samsung находит элемент.
Помогите поправить скрипт что-бы он не учитывал последовательность слов.
Вот кусок кода отвечающего за поиск:
SLS.isearch.on('keyup',function(){
			var results = []; 
			var toSearch = SLS.isearch.val();
			if(toSearch=='') { 
				SLS.prod = products;
			}else{
				for(var i=0; i<SLS.sproducts.length; i++) {
					if(SLS.sproducts[i].name.toLowerCase().indexOf(toSearch.toLowerCase())!=-1) {
					 results.push(SLS.sproducts[i]);
					}
				}
				SLS.prod = results;
			}
			SLS.page=1;
			SLS.products.html('');
			SLS.sorta(SLS.isort);
		});

рони 24.01.2019 22:00

Alexko64,
вы что-то не договариваите ...
alert("Samsung Galaxy".toLowerCase().indexOf("Galaxy".toLowerCase()) != -1);

Alexko64 24.01.2019 23:25

В каком смысле? Я не автор скрипта, что не так?
Скинул часть, вроде она отвечает за поиск, там еще кусок для сортировки.
Нужен целиком?

рони 25.01.2019 00:10

Alexko64,
могу только гадать, возможно в name продукта есть только Samsung
SLS.sproducts[i].name, Samsung Galaxy в другом месте.

Alexko64 25.01.2019 12:01

Цитата:

Сообщение от рони (Сообщение 502617)
Alexko64,
могу только гадать, возможно в name продукта есть только Samsung
SLS.sproducts[i].name, Samsung Galaxy в другом месте.

Вот полный скрипт с двумя примерами. В обоих примерах есть слова Samsung и Galaxy, но попробуйте ввести в поиск samsung galaxy или galaxy samsung. Скрипт учтет порядок слов и покажет только один результат или вовсе никакого

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<div>Search: <input type="search" class="simple-input" id="isearch" placeholder="Start typing...">
</div>
<br><br><br>
<div id="products"></div>
<script>
var products = [
	
	{name:'Samsung Galaxy s9',
	html:'<div>Samsung Galaxy S9</div>'
	},
		{name:'Galaxy S9 Samsung',
	html:'<div>Galaxy S9 Samsung</div>'
	},
	
	
	];
	</script>
	
	<script>
 	var SLS = {
	products: $('#products'),
	plow:$('#plow'),
	phigh:$('#phigh'),
	isearch:$('#isearch'),
	prod:products,
	sproducts: products,
	page:1,
	limit:12,
	isort:0,
	init:function(){
		SLS.pload(SLS.page,SLS.limit);
		SLS.events();
	},
	pload:function(page,limit){ 
		for(var i=((page-1)*limit);i<(limit*page);i++){ 
			if(SLS.prod[i])
				SLS.products.append($(SLS.prod[i].html));
		}
	},
	sorta:function(type){
			var byPrice = SLS.prod.slice(0);
			byPrice.sort(function(a,b) {
				return a.price - b.price;
			});
			if(type==1)
				byPrice=byPrice.reverse();
			SLS.prod=byPrice; 
			SLS.page=1;
			SLS.products.html('');
			SLS.pload(SLS.page,SLS.limit);
	},
	events:function(){
		$(window).on('scroll',function(){ 
			if($(this).scrollTop()+250>=($(document).height() - window.innerHeight-$('footer').height())){
				SLS.page++;
				SLS.pload(SLS.page,SLS.limit);
			}
		});
		SLS.plow.on('click',function(){
			SLS.isort=0;
			SLS.sorta(SLS.isort);
		});
		SLS.phigh.on('click',function(){
			SLS.isort=1;
			SLS.sorta(SLS.isort);
		});
		SLS.isearch.on('keyup',function(){
			var results = []; 
			var toSearch = SLS.isearch.val();
			if(toSearch=='') { 
				SLS.prod = products;
			}else{
				for(var i=0; i<SLS.sproducts.length; i++) {
					if(SLS.sproducts[i].name.toLowerCase().indexOf(toSearch.toLowerCase())!=-1) {
					 results.push(SLS.sproducts[i]);
					}
				}
				SLS.prod = results;
			}
			SLS.page=1;
			SLS.products.html('');
			SLS.sorta(SLS.isort);
		});
	}
};
SLS.init();
	</script>
</div>

laimas 25.01.2019 12:10

Цитата:

Сообщение от Alexko64
Скрипт учтет порядок слов и покажет только один результат

Все верно, ведь сравниваются строки. Производите сравнение рег. выражением, то есть по шаблону задаваемому значением в поле, где вместо пробела будет | - samsung|galaxy, тогда хоть так samsung|galaxy, хоть так galaxy|samsung, будет найдено все.

рони 25.01.2019 12:25

Alexko64,
<script>
function escapeRegExp(string){
    return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

function fnSearch(arr, pattern) {
        pattern = escapeRegExp(pattern);
        pattern = new RegExp("^(" + pattern.trim().replace(/\s+/g, "|") + ")", "i");
        return arr.filter(function(el) {
                return Object.values(el).some(function(val) {
                        return pattern.test(val)
                })
        })
};

var products = [

    {name:'Samsung Galaxy s9',
    html:'<div>Samsung Galaxy S9</div>'
    },
        {name:'Galaxy S9 Samsung',
    html:'<div>Galaxy S9 Samsung</div>'
    },


    ];
    </script>

    <script>
 	var SLS = {
    products: $('#products'),
    plow:$('#plow'),
    phigh:$('#phigh'),
    isearch:$('#isearch'),
    prod:products,
    sproducts: products,
    page:1,
    limit:12,
    isort:0,
    init:function(){
        SLS.pload(SLS.page,SLS.limit);
        SLS.events();
    },
    pload:function(page,limit){
        for(var i=((page-1)*limit);i<(limit*page);i++){
            if(SLS.prod[i])
                SLS.products.append($(SLS.prod[i].html));
        }
    },
    sorta:function(type){
            var byPrice = SLS.prod.slice(0);
            byPrice.sort(function(a,b) {
                return a.price - b.price;
            });
            if(type==1)
                byPrice=byPrice.reverse();
            SLS.prod=byPrice;
            SLS.page=1;
            SLS.products.html('');
            SLS.pload(SLS.page,SLS.limit);
    },
    events:function(){
        $(window).on('scroll',function(){
            if($(this).scrollTop()+250>=($(document).height() - window.innerHeight-$('footer').height())){
                SLS.page++;
                SLS.pload(SLS.page,SLS.limit);
            }
        });
        SLS.plow.on('click',function(){
            SLS.isort=0;
            SLS.sorta(SLS.isort);
        });
        SLS.phigh.on('click',function(){
            SLS.isort=1;
            SLS.sorta(SLS.isort);
        });
        SLS.isearch.on('keyup',function(){
            var results = [];
            var toSearch = SLS.isearch.val();
            if(toSearch=='') {
                SLS.prod = products;
            }else{

                SLS.prod = fnSearch(SLS.sproducts, toSearch);

            }
            SLS.page=1;
            SLS.products.html('');
            SLS.sorta(SLS.isort);
        });
    }
};
SLS.init();
    </script>

Alexko64 25.01.2019 12:29

Цитата:

Сообщение от laimas (Сообщение 502638)
Все верно, ведь сравниваются строки. Производите сравнение рег. выражением, то есть по шаблону задаваемому значением в поле, где вместо пробела будет | - samsung|galaxy, тогда хоть так samsung|galaxy, хоть так galaxy|samsung, будет найдено все.

К сожалению я не знаю как это сделать, по этому и обратился за помощью. Если вы можете написать что на что заменить то буду очень благодарен

Alexko64 25.01.2019 12:30

пока писал ответ не увидел что уже ответили )

laimas 25.01.2019 12:32

рони, надо бы наверное trim() для входного значения, и \s более одного?

Видимо перепил, trim есть. :)

рони 25.01.2019 12:38

laimas,
если работает(сработает) лучше не трогать :)

laimas 25.01.2019 12:40

рони, ну если ввод к примеру будет таким
' Galaxy     samsung  '
, то шаблон получится такой
'Galaxy|    samsung'
.

Alexko64 25.01.2019 12:58

Цитата:

Сообщение от рони (Сообщение 502650)
laimas,
если работает(сработает) лучше не трогать :)

Не повезло)
Не работает, при поиске Samsung, второй вариант что начинается на Galaxy уже не находит

laimas 25.01.2019 13:01

Цитата:

Сообщение от Alexko64
второй вариант что начинается на Galaxy уже не находит

Что-то какая-то тайна покрытая мраком, ну не может быть такого. :)

var s = 'Samsung Galaxy', //в чем ищем
    v = ' Galaxy     samsung  ', //пусть ввели так
    r = new RegExp(v.trim().replace(/\s+/, '|'), 'gi'); //определяем шаблон поиска

alert(r.test(v)) //есть совпадение?


И если строка в которой ищем будет 'Galaxy Samsung', все равно будет найдено совпадение.

PS. рони подправил код, а вы скопировали до правки, проверьте еще раз, может в этом причина.

Alexko64 25.01.2019 13:10

Цитата:

Сообщение от laimas (Сообщение 502655)
Что-то какая-то тайна покрытая мраком, ну не может быть такого. :)

var s = 'Samsung Galaxy', //в чем ищем
    v = ' Galaxy     samsung  ', //пусть ввели так
    r = new RegExp(v.trim().replace(/\s+/, '|'), 'gi'); //определяем шаблон поиска

alert(r.test(v)) //есть совпадение?


И если строка в которой ищем будет 'Galaxy Samsung', все равно будет найдено совпадение.

Лучше один раз увидеть чем 100 раз услышать )
Вот залил на тест хост
http://mobidor.zzz.com.ua/test.php

Alexko64 25.01.2019 13:11

Цитата:

Сообщение от laimas (Сообщение 502655)

PS. рони подправил код, а вы скопировали до правки, проверьте еще раз, может в этом причина.

Момент...

Alexko64 25.01.2019 13:14

Ну какая тайна, разбираем-же пример. не работает, на хостинге можно проверить где я залил
http://mobidor.zzz.com.ua/test.php

laimas 25.01.2019 13:31

А так:

pattern = new RegExp(pattern.trim().replace(/\s+/g, "|"), "gi"); 
return arr.filter(function(el) {
                //return Object.values(el).some(function(val) {
                        return pattern.test(Object.values(el)[0])
                //})
        })


А отображать и надо одно совпадение, которое начинается с первого введенного в поиске?

рони 25.01.2019 13:40

Alexko64,
замените
function fnSearch(arr, pattern) {
        pattern = escapeRegExp(pattern);
        pattern = new RegExp("(" + pattern.trim().replace(/\s+/g, "|") + ")", "i");
        return arr.filter(function(el) {
                pattern.lastIndex = 0;
                return pattern.test(el.name)
        })
};

Alexko64 25.01.2019 13:42

Цитата:

Сообщение от laimas (Сообщение 502660)
А так:

pattern = new RegExp(pattern.trim().replace(/\s+/g, "|"), "gi"); 
return arr.filter(function(el) {
                //return Object.values(el).some(function(val) {
                        return pattern.test(Object.values(el)[0])
                //})
        })


А отображать и надо одно совпадение, которое начинается с первого введенного в поиске?

Не работает. Перезалил тест, посмотрите. Тееперь там все плохо.
попробуйте найти Galaxy, Samsung note 6 (его там и в помине нет но поиск выдает результат)
Добавил RAM для отличий между вариантами.
Отображать надо все совпадения которые подходят под шаблон, как в любом поиске

laimas 25.01.2019 13:48

Цитата:

Сообщение от Alexko64
попробуйте найти Galaxy, Samsung note 6

Тут надо определится с поиском, либо вы ищите точное совпадение, то есть режим "И", либо одно из совпадений, то есть режим "ИЛИ". Ибо баг при поиске по совпадению строк предполагает поиск в режиме ИЛИ, что и достигается рег выражением. Теперь же речь идет о "Galaxy, Samsung note 6", которого нет, и его не должно быть, а это уже означает режим И, фактически полное совпадение строк.

Malleys 25.01.2019 13:52

function fnSearch(arr, pattern) {
	var parts = pattern.trim().toLowerCase().split(/\s+/);
	
	return arr.filter(function(product) {
		return parts.every(function(part) {
			return product.name.toLowerCase().includes(part)
		});
	});
}


Кстати вы можете менять some на every, посмотрите, может вам нужен такой эффект!

Alexko64 25.01.2019 13:57

Цитата:

Сообщение от рони (Сообщение 502664)
Alexko64,
замените
function fnSearch(arr, pattern) {
        pattern = escapeRegExp(pattern);
        pattern = new RegExp("(" + pattern.trim().replace(/\s+/g, "|") + ")", "i");
        return arr.filter(function(el) {
                pattern.lastIndex = 0;
                return pattern.test(el.name)
        })
};

Работает, но ищет совпадения любого из слов в запросе по отдельности.
Перезалил на хост. При поиске Samsung 2GB находит все где есть слово samsung и все где есть 2GB.

Я так понимаю что алгоритм поиска должен быть такой:
Показывать все результаты где было совпадение всех слов введенных в запросе но не обращать внимание на порядок слов в искомой строке.

laimas 25.01.2019 14:00

НУ может быть тогда такое как Malleys написал?

рони 25.01.2019 14:00

Цитата:

Сообщение от Alexko64
где было совпадение всех слов введенных

пост №22 замените some на every

Alexko64 25.01.2019 14:03

Цитата:

Сообщение от Malleys (Сообщение 502669)
function fnSearch(arr, pattern) {
	return arr.filter(function(product) {
		return toSearch
			.trim()
			.toLowerCase()
			.split(/\s+/)
			.some(function(p) {
				return product.name.toLowerCase().includes(p)
			});
	});
}


Кстати вы можете менять some на every, посмотрите, может вам нужен такой эффект!

Попробовал, поиск перестал работать совсем, если не слоэно скиньте целым куском с моим кодом, а то я уже запутался к какому варианту относится ваша правка

Alexko64 25.01.2019 14:05

Залил на хостинг вариант с правкой Malleys
Не работает поиск совсем
http://mobidor.zzz.com.ua/test.php

Malleys 25.01.2019 14:06

Последний раз редактировалось Malleys, Сегодня в 13:05. Причина: Если вы смотрели решение до 10:56 по Гринвичу, то посмотрите ещё раз.

рони 25.01.2019 14:06

Alexko64,
function fnSearch(arr, pattern) {
    return arr.filter(function(product) {
        return pattern
            .trim()
            .toLowerCase()
            .split(/\s+/)
            .every(function(p) {
                return product.name.toLowerCase().includes(p)
            });
    });
}


var products = [

    {name:'Samsung Galaxy s9',
    html:'<div>Samsung Galaxy S9</div>'
    },
        {name:'Galaxy S9 Samsung',
    html:'<div>Galaxy S9 Samsung</div>'
    },


    ];
    </script>

    <script>
 	var SLS = {
    products: $('#products'),
    plow:$('#plow'),
    phigh:$('#phigh'),
    isearch:$('#isearch'),
    prod:products,
    sproducts: products,
    page:1,
    limit:12,
    isort:0,
    init:function(){
        SLS.pload(SLS.page,SLS.limit);
        SLS.events();
    },
    pload:function(page,limit){
        for(var i=((page-1)*limit);i<(limit*page);i++){
            if(SLS.prod[i])
                SLS.products.append($(SLS.prod[i].html));
        }
    },
    sorta:function(type){
            var byPrice = SLS.prod.slice(0);
            byPrice.sort(function(a,b) {
                return a.price - b.price;
            });
            if(type==1)
                byPrice=byPrice.reverse();
            SLS.prod=byPrice;
            SLS.page=1;
            SLS.products.html('');
            SLS.pload(SLS.page,SLS.limit);
    },
    events:function(){
        $(window).on('scroll',function(){
            if($(this).scrollTop()+250>=($(document).height() - window.innerHeight-$('footer').height())){
                SLS.page++;
                SLS.pload(SLS.page,SLS.limit);
            }
        });
        SLS.plow.on('click',function(){
            SLS.isort=0;
            SLS.sorta(SLS.isort);
        });
        SLS.phigh.on('click',function(){
            SLS.isort=1;
            SLS.sorta(SLS.isort);
        });
        SLS.isearch.on('keyup',function(){
            var results = [];
            var toSearch = SLS.isearch.val();
            if(toSearch=='') {
                SLS.prod = products;
            }else{

                SLS.prod = fnSearch(SLS.sproducts, toSearch);
                
            }
            SLS.page=1;
            SLS.products.html('');
            SLS.sorta(SLS.isort);
        });
    }
};
SLS.init();

Alexko64 25.01.2019 14:14

Урааа) заработало
Вы лучшие. Плюсики в карму всем кто старался.
Залил на хост рабочий вариант, можете глянуть

Alexko64 25.01.2019 19:51

Парни, упустил один момент. В тексте по которому идет поиск, присутствуют html теги, соответственно поиск их учитывает, попытался решить проблему вот так
function fnSearch(arr, pattern) {
    return arr.filter(function(product) {
        return pattern
	         .replace(/\<.*?>/g, "")
            .trim()
            .toLowerCase()
            .split(/\s+/)
            .every(function(p) {
                return product.html.toLowerCase().includes(p)
            });
    });
}

не помогло, потом вот так
function fnSearch(arr, pattern) {
    return arr.filter(function(product) {
        return pattern
	      .text()
            .trim()
            .toLowerCase()
            .split(/\s+/)
            .every(function(p) {
                return product.html.toLowerCase().includes(p)
            });
    });
}

не сработало
Подсобите еще разок?

рони 25.01.2019 19:59

Alexko64,
name чем не устроило?

рони 25.01.2019 20:05

Alexko64,
function fnSearch(arr, pattern) {
    return arr.filter(function(product) {
        return pattern
            .trim()
            .toLowerCase()
            .split(/\s+/)
            .every(function(p) {
                return $("<div/>", {html : product.html}).text().toLowerCase().includes(p)
            });
    });
}

Alexko64 25.01.2019 20:35

Цитата:

Сообщение от рони (Сообщение 502712)
Alexko64,
name чем не устроило?

Решил уменьшить количество кода, не дублировать данные , убрпть name совсем и искать по html

Alexko64 26.01.2019 19:55

Нашел баг или вернее специфику работы скрипта. Он не учитывает место нахождения первой буквы из запроса.
Если есть такие вариенты на сайте:

Samsung Galaxy Note 5 4GB RAN 64GB ROM
Samsung Galaxy Note 6 2GB RAM 128 ROM
Samsung Galaxy Note 7 4GB RAM 256 GB ROM
SAmsung Galaxy Note 8 6GB RAM 512 GB ROM

И поискать Samsung Galaxy Note 6, то скрипт покажет все результаты, потому-что везде есть цифра 6 и не важно в каком месте она находится.
Аналогично скрипт покажет все результаты на любой из следующих запросов:
sung alaxy, laxy 6 и тд...
Для наглядности залил пример на хостинг
http://mobidor.zzz.com.ua/test.php

рони 26.01.2019 20:18

Alexko64,
медитируйте над постом №7, и на потом, лучше формулируйте условия.

laimas 26.01.2019 20:35

Alexko64, ну тут палка о двух концах, если говорить о цифрах.

Samsung Galaxy Note 5 4GB RAN 64GB ROM - здесь 6, но это размер и полный 64
Samsung Galaxy Note 6 2GB RAM 128 ROM - здесь 6, но модель

Как понять при вводе чего ищет пользователь - модель или размер. Пусть угадали, размер, но и 64 не равно 64GB. К тому же размер ведь не только у ROM, но и RAM, что нужно возвращать если не указано GB (128 ROM) - нет или где попадется подходящее?

Вообще все это похоже на фильтр, вот только фильтр всегда определяет не только значения, но и какому параметру они принадлежат, в строке же искать такое задача нелегкая.

PS. Да еще если строки имеют ошибки, как у вас, не обязательно, что это так и есть, но от них никто не застрахован.

рони 26.01.2019 20:39

Alexko64,
function escapeRegExp(string){
    return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

function fnSearch(arr, pattern) {
    return arr.filter(function(product) {
             var text = $("<div/>", {html : product.html}).text().toLowerCase();
        return pattern
            .trim()
            .toLowerCase()
            .split(/\s+/)
            .every(function(p) {
                p = escapeRegExp(p);
                p = new RegExp("(^|\\s)"  + p, "i");
                return p.test(text)
            });
    });
}

Alexko64 26.01.2019 21:16

Цитата:

Сообщение от laimas (Сообщение 502753)
Alexko64, ну тут палка о двух концах, если говорить о цифрах.

Samsung Galaxy Note 5 4GB RAN 64GB ROM - здесь 6, но это размер и полный 64
Samsung Galaxy Note 6 2GB RAM 128 ROM - здесь 6, но модель

Как понять при вводе чего ищет пользователь - модель или размер. Пусть угадали, размер, но и 64 не равно 64GB. К тому же размер ведь не только у ROM, но и RAM, что нужно возвращать если не указано GB (128 ROM) - нет или где попадется подходящее?

Вообще все это похоже на фильтр, вот только фильтр всегда определяет не только значения, но и какому параметру они принадлежат, в строке же искать такое задача нелегкая.

PS. Да еще если строки имеют ошибки, как у вас, не обязательно, что это так и есть, но от них никто не застрахован.

Согласитесь что 6 это не 64, а 64 не равно 64GB, по этому логично ожидать что в результатах поиска их не будет. Я виже решение в сравнении жесткого соответствия каждого слова, тоисть not=not, но not не равно note, или возможно включить пробел как елемент играющий роль в запросе, тогда если после 6 поставить пробел то оно не будет равно 64...как то так. В любом случае мне кажется что вы мне помогли уже больше чем я мог надеятся по этому спасибо за все.

laimas 27.01.2019 19:30

Цитата:

Сообщение от Alexko64
логично ожидать что в результатах поиска их не будет

А польза от такого поиска если то чего ищут имеется, но не выдается? Получается, что это "как повезет", будут подбирать по объему ROM и вводить только объем без единиц, а записи только с GB, неудача, или же наоборот, или часть.

Уж тогда организовывать на клиенте фильтр, как это бы был запрос в базу по параметрам. Искать не в строке, а объектах:

{
    brand: "Samsung",
    line: "Galaxy Note",
    model: 5,
    ram: 4,
    rom: 64
},
....


Тогда проблем с подбором не будет. Главная проблема, это сделать интерактивное поле для поиска (если поиск, а не фильтр с выбором параметров), которое бы по мере ввода предлагало выбор следующего параметра, следующего, сопоставляя каждый ввод с предложенным параметром, и так до завершения ввода. В итоге нужно искать среди параметров объектов совпадения из полученного при вводе объекта.


Часовой пояс GMT +3, время: 08:25.