Configurar o Sphinx Search para uma loja online

Não há tanta informação sobre a Sphinx quanto gostaríamos. Artigo em excesso não dói.
Os primeiros passos no desenvolvimento do Sphinx me ajudaram a criar os artigos Criando um mecanismo de pesquisa introdutório no Sphinx + php e no Sphinx Example search em um projeto real - loja de autopeças Tecdoc Eu aconselho você a começar com eles.


Por algum tempo, uma pesquisa em LIKE para cada palavra da consulta funcionou no meu site. Eu queria mais, e aqui estão alguns casos que agora serão tratados corretamente:


  • Formas de palavras. A saída para "parafusos" e "parafusos" deve ser a mesma.
  • Pesquise por fragmento de palavra.
  • Procure por números não inteiros. Ponto separador e vírgula.
  • Letra y
  • Erros comuns. Por exemplo, "Amortecedor".
  • Sinônimos Regulador e ESC.
  • Idioma. mAh e mAh, B e V, AAA latino e cirílico.
  • Palavra feita de letras e números. 10x15x4, 6000mAh

Seção de origem e classificação opcional


O problema deve primeiro conter itens em estoque, depois temporariamente ausentes e depois arquivados. E todos os três desses grupos devem ser classificados por relevância. Para fazer isso, você precisa definir os atributos. No meu caso, esses são os campos clear e in_stock da seção sphinx.conf de origem


sql_query = \ SELECT id, `art`, `name`, `clearance`, `in_stock` \ FROM items_zip WHERE show_flag=1 sql_attr_bool = clearance sql_attr_uint = in_stock 

Esses campos serão usados ​​na geração de saída em PHP. Vou descrever abaixo.


Seção de índice no sphinx.conf


morfologia = stem_enru
A morfologia resolve meu primeiro problema. Uma pesquisa por 'rolamentos', 'rolamentos', 'rolamentos' levará a um único resultado.


Os caules (stem_enru) são mais rápidos, os lemas (lemmatize_ru) são mais precisos. Eu tentei apenas golpes. A escolha afetará seu dicionário de substituições de formas de palavras. Quer mudar - você precisa reescrever.


min_word_len = 1
Índice de palavras de qualquer tamanho.


html_strip = 1
Remover tags html


min_infix_len = 1
A pesquisa estará em um fragmento da palavra. Fragmentos de índice de até 1 letra. Como tenho menos de 10.000 itens no banco de dados, não salvo no índice.


expand_keywords = 1
Conduz automaticamente a consulta para o formulário "(executando | executando | = executando)". min_infix_len e expand_keywords farão com que o RV 2205 emita o RV2205. A propósito, um traço é um separador equivalente a um espaço. Portanto, o RV-2205 dará o mesmo RV2205.


charset_table = 0..9, A..Z-> a..z, _, a..z, U + 410..U + 42F-> U + 430..U + 44F, U + 430..U + 44F, U + 401-> U + 0435, U + 451-> U + 0435
Trazemos o alfabeto latino e o alfabeto cirílico em letras minúsculas. substitua por e.


blend_chars = +, &, U + 2C, U + 2E
Eu tenho muitos números não inteiros. Eles precisam ser totalmente indexados. U + 2C e U + 2E são um ponto e uma vírgula. Por exemplo, 1,25 serão indexados como '1,25', '1' e '25'.


regexp_filter = (\ d +) \, (\ d +) => \ 1. \ 2
Casas decimais em números podem ser separadas por pontos e vírgulas: "1,75", "1,75". Trazemos tudo ao ponto


Sinônimos e erros de digitação


As unidades de medida podem ser escritas em russo ou inglês: mm-mm, mAh-mAh, mW-mW. Adicione ao dicionário de sinônimos, o caminho especificado nas formas de palavras: "mach> mah". Eu escolho o idioma para o índice de acordo com minhas próprias preferências.


O sinal ~ indica a aplicação da substituição após o manipulador de morfologia. Isso permite que você não escreva todas as formas de palavras e, em vez das regras para 'crosta', 'crosta', 'crosta', escreva "~ cork> body"


Minha lista está completa:


 ~ > esc  > esc  > mah ~ >  ~ >  ~ > buzz ~ > buzz ~ > buzz ~ > buzz ~ > buzz ~ >  ~ >  ~ >  li-po > lipo ~ >  ~ >   >   >  vtx >  ~ >  lollipop > lolipop battery >  ~ >  ~ >  ~ >  mkF >   > BEC  > BEC ~ >  LED >  ~ >  driver >  ~ >  ~ >   > AAA  > AA  > M mm >   > mW  > V  > A deans > t-plug tplug > t-plug 

Colocar letras em números


Às vezes, os números fazem parte do nome (por exemplo, LCD5208D), mas mais frequentemente uma característica (100mAh, 10x15x4mm). Separe todos os números das letras e do índice.


Isso resolverá vários problemas:


  • Alguém estará procurando 'rolamento 10x15x4', alguém 'rolamento 15x10x4'. Os números indexados resultarão na saída correta.
  • As unidades de medida podem ou não ser separadas por um espaço do número: "1,75 mm", "1,75 mm".
  • Para títulos, isso também é útil. A saída correta estará nas três opções de gravação LCD-5208, LCD 5208 e LCD5208

Antes de escrever uma expressão regular para separar números, é necessário unificar os delimitadores. É importante lembrar que expressões regulares são executadas tudo e sequencialmente.


Removemos o x, ele e a estrela em tamanhos como 10x4x4 M3x10:


 regexp_filter = (\d+)[x\x{0445}\*] => \1 x 

Solte as caudas:


 regexp_filter = (\d*\.?\d+)(\D+) => \1 \2 

E as cabeças:


 regexp_filter = (\D+)(\d*\.?\d+) => \1 \2 

Nós descartamos "mm", pois eles geralmente não são indicados no nome do produto.
Crie um arquivo stop.txt e escreva-o com palavras irrelevantes.
Conteúdo:


  mm 

Agora um pouco sobre PHP


Sphinxapi será privado mais cedo ou mais tarde. Vamos usar o Sphinxql. Para fazer isso, conecte-se ao banco de dados. No meu caso, o Sphinx está conectado via hospedagem, fica assim:


 $opt = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => TRUE, ); $dsn = 'mysql:host=127.0.0.1;port=9306;'; $this->pdo = new PDO($dsn, DB_USER, DB_PASS, $opt); 

E toda a comunicação com o Spinxql é um SELECT transmitindo texto de consulta filtrado


 $stmt = $this->pdo->prepare("SELECT `id`, WEIGHT() as `w`, in_stock>0 AS stock FROM `items` WHERE MATCH ('".$search."') ORDER BY clearance ASC, stock DESC, w DESC LIMIT ".$limit." OPTION field_weights=(name=10, art=3, cat_names=3, model_names=3)"); 

O SphinxQL não entende as expressões na seção de classificação ORDER BY, então WEIGHT () e in_stock> 0 tiveram que ser colocados em campos. A propósito, o limite padrão é de apenas 20.


A classificação produzirá primeiro itens em estoque, depois temporariamente ausentes e depois arquivados. E todos esses três grupos serão classificados por relevância (peso).


Através de field_weights, definimos quais campos terão mais peso.


Ao concluir a solicitação, obtemos uma matriz de identificação classificada. Infelizmente, a seleção de dados através de WHERE id IN () violará essa classificação. Tem que formar sua solicitação para cada ID.


Na fase de depuração , a consulta SHOW META imediatamente após a consulta SELECT ajuda muito. Especialmente para verificar formas de palavras do dicionário e filtros de expressão regular. Você pode ver a lista de palavras-chave nas quais a consulta foi expandida.


Complicando sql_query


Nós vendemos peças de reposição. Decidi adicionar o nome da categoria do produto e o nome do modelo para o qual a peça de reposição deve ser adicionada ao índice. Mas cada produto pode ser vinculado a várias categorias ao mesmo tempo e ser adequado para vários modelos. E descobri a função GROUP_CONCAT, que permite obter dados agrupando-os em uma string. Por exemplo, o campo categories.name conterá todas as categorias do items_zip.id selecionado, separadas por espaços.


 SELECT items_zip.id, `art`, items_zip.`name`, `clearance`, `in_stock`, GROUP_CONCAT(DISTINCT categories.name SEPARATOR ' ') AS cat_names, GROUP_CONCAT(DISTINCT items.family SEPARATOR ' ') AS model_names FROM items_zip LEFT JOIN items_cat ON items_cat.item_id=items_zip.id LEFT JOIN categories ON categories.id=items_cat.cat_id LEFT JOIN zip_comp ON zip_comp.zip_id=items_zip.id LEFT JOIN items ON zip_comp.model_id=items.id WHERE items_zip.show_flag=1 GROUP BY items_zip.id 

Source: https://habr.com/ru/post/pt439018/


All Articles