Pular para o conteúdo principal

Desafio 42: Consultas de Pesquisa — Sintaxe e Filtros

Tempo Estimado

45-60 min | Custo: ~$0.10 (consultas em índice existente) | Domínio: Knowledge Mining & Extraction (15-20%)

Habilidades do exame cobertas

HabilidadePeso
Consultar um índice usando sintaxe simplesAlto
Consultar um índice usando sintaxe Lucene completaAlto
Aplicar filtros com expressões ODataAlto
Implementar ordenação, paginação e seleção de camposMédio
Implementar navegação facetadaMédio
Usar wildcards e pesquisa fuzzyMédio

Visão Geral

O Azure AI Search suporta dois analisadores de consulta:

AnalisadorSintaxeCaso de uso
Simple (padrão)+term -term "phrase" *suffixCaixas de pesquisa para usuários
Full Lucenefield:term~2 /regex/ term^boostConsultas avançadas para desenvolvedores

Parâmetros de consulta principais:

  • search: O texto de pesquisa (sintaxe simples ou Lucene)
  • $filter: Expressão de filtro OData para correspondência exata
  • $orderby: Ordenar resultados
  • $select: Escolher quais campos retornar
  • $top / $skip: Paginação
  • $count: Incluir contagem total na resposta
  • facets: Agregar valores de campo para navegação

Pré-requisitos

  • Desafio 40 concluído (índice com documentos enriquecidos)
  • Python 3.9+ com azure-search-documents>=11.4.0
  • .NET 8 com Azure.Search.Documents
  • Pelo menos 10+ documentos indexados para resultados significativos

Implementação

Tarefa 1: Sintaxe de consulta simples

from azure.core.credentials import AzureKeyCredential
from azure.search.documents import SearchClient

endpoint = f"https://{SEARCH_SERVICE}.search.windows.net"
credential = AzureKeyCredential(SEARCH_KEY)
search_client = SearchClient(endpoint=endpoint, index_name="documents-index", credential=credential)

# Simple search — finds documents containing "Azure" AND "cognitive"
results = search_client.search(
search_text="Azure cognitive",
include_total_count=True,
top=5
)

print(f"Total matching documents: {results.get_count()}")
for result in results:
print(f" Score: {result['@search.score']:.4f} | {result['metadata_storage_name']}")

# Phrase search — exact phrase match
results = search_client.search(search_text='"Azure AI services"')
for result in results:
print(f" Phrase match: {result['metadata_storage_name']}")

# Boolean operators in simple syntax (+ required, - excluded, | OR)
results = search_client.search(search_text="+Azure -deprecated | cognitive")

Tarefa 2: Sintaxe Lucene completa

from azure.search.documents.models import QueryType

# Fuzzy search — finds "cognitive" even if user types "cogntive" (edit distance 1)
results = search_client.search(
search_text="cogntive~1",
query_type=QueryType.FULL
)
for result in results:
print(f" Fuzzy match: {result['metadata_storage_name']}")

# Wildcard search — prefix matching
results = search_client.search(
search_text="micro*",
query_type=QueryType.FULL
)

# Proximity search — "Azure" and "services" within 3 words of each other
results = search_client.search(
search_text='"Azure services"~3',
query_type=QueryType.FULL
)

# Boosted terms — "AI" is 4x more important than "cloud"
results = search_client.search(
search_text="AI^4 cloud",
query_type=QueryType.FULL
)

# Field-scoped search — search only in keyphrases field
results = search_client.search(
search_text="keyphrases:machine learning",
query_type=QueryType.FULL
)

Tarefa 3: Filtros OData

# Filter by language
results = search_client.search(
search_text="*",
filter="language eq 'en'",
include_total_count=True
)
print(f"English documents: {results.get_count()}")

# Filter with collection — any keyphrase matches
results = search_client.search(
search_text="*",
filter="keyphrases/any(k: k eq 'machine learning')"
)

# Combine search + filter
results = search_client.search(
search_text="Azure",
filter="language eq 'en' and wordCount gt 100",
order_by=["wordCount desc"],
select=["metadata_storage_name", "language", "wordCount"]
)

for result in results:
print(f" {result['metadata_storage_name']} | Words: {result.get('wordCount', 'N/A')}")

# Comparison operators: eq, ne, gt, ge, lt, le
# Logical operators: and, or, not
# Collection operators: any(), all()
# Functions: search.in(), geo.distance(), geo.intersects()
results = search_client.search(
search_text="*",
filter="search.in(language, 'en,fr,de', ',')"
)

Tarefa 4: Paginação e seleção de campos

# Paginated results — page 1 (items 1-10)
page1 = search_client.search(
search_text="*",
top=10,
skip=0,
include_total_count=True,
select=["metadata_storage_name", "language", "keyphrases"]
)
print(f"Total: {page1.get_count()}")
for doc in page1:
print(f" {doc['metadata_storage_name']}")

# Page 2 (items 11-20)
page2 = search_client.search(
search_text="*",
top=10,
skip=10,
select=["metadata_storage_name", "language", "keyphrases"]
)

# Sorting by multiple fields
results = search_client.search(
search_text="*",
order_by=["language asc", "metadata_storage_name asc"],
top=20
)

Tarefa 5: Navegação facetada

# Facets — aggregate values for building filter UI
results = search_client.search(
search_text="*",
facets=["language,count:10", "keyphrases,count:20"],
include_total_count=True
)

print(f"Total results: {results.get_count()}")
print("\nLanguage facets:")
for facet in results.get_facets().get("language", []):
print(f" {facet['value']}: {facet['count']} documents")

print("\nTop keyphrases:")
for facet in results.get_facets().get("keyphrases", []):
print(f" {facet['value']}: {facet['count']} documents")

# Combine facets with a filter (drill-down)
results = search_client.search(
search_text="*",
filter="language eq 'en'",
facets=["keyphrases,count:10"],
)
print("\nTop keyphrases (English only):")
for facet in results.get_facets().get("keyphrases", []):
print(f" {facet['value']}: {facet['count']}")

Saída Esperada

{
"@odata.count": 42,
"@search.facets": {
"language": [
{"value": "en", "count": 35},
{"value": "fr", "count": 4},
{"value": "de", "count": 3}
],
"keyphrases": [
{"value": "machine learning", "count": 12},
{"value": "Azure AI", "count": 10},
{"value": "cognitive services", "count": 8}
]
},
"value": [...]
}

Quebra & conserta

#CenárioSintomaCausa RaizCorreção
1Filtro em campo não filtrávelHTTP 400: "Field 'content' is not filterable"O campo content foi definido com filterable: falseAtualize o esquema do índice para adicionar filterable: true ou filtre em um campo que seja filtrável
2Faceta em campo não facetávelHTTP 400: "Field is not facetable"O campo não possui o atributo facetable na definição do índiceAtualize o índice para tornar o campo facetável (requer re-indexação se mudar o tipo)
3Sintaxe Lucene completa não funcionaWildcards/fuzzy tratados como texto literalqueryType=full ausente — o padrão é simpleDefina query_type=QueryType.FULL (Python) ou QueryType = SearchQueryType.Full (C#)
4$orderby falha"Cannot sort on field 'keyphrases'"Campos de coleção (Collection(Edm.String)) não podem ser ordenadosOrdene apenas por campos escalares; use perfis de pontuação para ajuste de relevância
5Paginação retorna duplicatasMesmos documentos aparecem em páginas diferentesO índice foi modificado entre as requisições de página; use tokens de continuação para consistênciaUse search_after para paginação profunda ou aceite consistência eventual

Verificação de Conhecimento

1. Você quer que os usuários pesquisem 'programing' e ainda encontrem documentos contendo 'programming'. Qual sintaxe de consulta suporta isso?

2. Você precisa filtrar documentos onde QUALQUER keyphrase seja igual a 'machine learning'. Qual filtro OData está correto?

3. Qual é o valor máximo permitido para $skip no Azure AI Search?

4. Um campo é definido como 'searchable: true, filterable: false, facetable: true'. Qual operação irá FALHAR?

5. Você configura facets=['language,count:5']. O que o parâmetro 'count:5' controla?

Limpeza

Nenhum recurso adicional foi criado neste desafio (usa o índice existente do Desafio 40).

# If you want to clean up everything:
az group delete --name rg-ai102-search --yes --no-wait

Saiba Mais