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