the holy quest for the holy home
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

16 KiB

Esquema físico do banco dfimoveis.sqlite3

Atualização de 22/07/2026: as seções históricas deste documento descrevem o schema v1. A migração aditiva v2 descrita na seção final prevalece para consultas atuais.

1. Escopo e identificação do snapshot

Este documento descreve o esquema observado e revalidado em 21 de julho de 2026. Ele não altera o modelo conceitual de docs/DATA_MODEL.md e não atribui significado não comprovado às colunas.

Item Valor
Caminho dfimoveis_data/dfimoveis.sqlite3
Tamanho 9.478.144 bytes
SHA-256 bfbbdd09e88f76afd917ebd981b848b9321aeb5fc0602f712645d33e1a5ea76c
Data de referência 2026-07-21
Codificação UTF-8
journal_mode persistido WAL
Páginas 2.314 páginas de 4.096 bytes; freelist_count = 2
user_version / application_id 0 / 0
Método de abertura URI mode=ro, PRAGMA query_only = ON e transação de leitura

Como este arquivo é atualizado pelo scraper, não foi usado immutable=1. PRAGMA quick_check retornou ok e PRAGMA foreign_key_check não retornou violações. Nenhum sidecar foi removido.

2. Visão geral

O banco possui cinco tabelas da aplicação e a tabela interna sqlite_sequence. Não há views nem triggers. Os quatro índices existentes são autoíndices criados por chaves primárias ou restrições UNIQUE.

erDiagram
    listings ||--|{ listing_history : "listing_id"
    listings ||--|{ listing_searches : "listing_id"
    listings ||--|{ photos : "listing_id"
    crawl_runs {
        INTEGER id PK
        TEXT started_at
        TEXT finished_at
        TEXT status
        TEXT stats_json
    }
    listings {
        TEXT listing_id PK
        TEXT url
        REAL price_brl
        REAL area_m2
        TEXT first_seen_at
        TEXT last_seen_at
        TEXT content_hash
    }
    listing_history {
        INTEGER id PK
        TEXT listing_id FK
        TEXT captured_at
        TEXT content_hash
        TEXT data_json
    }
    listing_searches {
        TEXT listing_id PK,FK
        TEXT search_url PK
        TEXT first_seen_at
        TEXT last_seen_at
    }
    photos {
        TEXT listing_id PK,FK
        TEXT source_url PK
        INTEGER ordinal
        TEXT sha256
        TEXT local_path
    }

crawl_runs não possui relacionamento físico com anúncios ou snapshots. Portanto, a execução que gerou uma linha só pode ser inferida pelos horários, não recuperada por uma chave.

Contagens e cardinalidades observadas

Tabela Linhas Interpretação física
crawl_runs 20 Execuções do coletor
listings 161 Identidades de anúncios e seu estado corrente
listing_history 562 Estados distintos dos anúncios por hash
listing_searches 161 Vínculos entre anúncio e URL de busca
photos 7.363 Mídias associadas diretamente aos anúncios
sqlite_sequence 2 Sequências internas de tabelas AUTOINCREMENT

No snapshot avaliado:

  • cada anúncio está ligado a exatamente uma URL de busca;
  • cada anúncio possui de 1 a 4 registros em listing_history;
  • cada anúncio possui de 5 a 79 linhas em photos;
  • não há órfãos nas três relações com listings;
  • o hash corrente de cada anúncio possui registro correspondente no histórico;
  • grande parte dos snapshots adicionais reflete instabilidade de campos contaminados, sobretudo creci, e não mudança imobiliária real.

3. Dicionário de tabelas

3.1 crawl_runs

Representa execuções técnicas do coletor. Não registra fonte, versão do scraper, configuração, texto de erro ou relacionamento direto com os itens coletados.

Coluna Tipo Nulo? Chave/default Interpretação
id INTEGER Não PK, AUTOINCREMENT Identificador da execução; a PK inteira atribui um valor quando omitido
started_at TEXT Não Início em ISO 8601, observado em UTC
finished_at TEXT Sim Término em ISO 8601
status TEXT Não Estado textual da execução; ok e failed foram observados
stats_json TEXT Não '{}' Contadores técnicos em JSON

As estruturas JSON observadas são válidas. Além das chaves originais, execuções recentes também registram photos_skipped.

3.2 listings

Representa a identidade do anúncio no portal e uma cópia mutável de seu estado mais recente. Não representa um imóvel físico deduplicado.

Coluna Tipo Nulo? Chave/default Interpretação e ressalvas
listing_id TEXT Sim no DDL; não observado PK Identificador do anúncio no portal; todos os valores atuais têm 6–7 dígitos. Por peculiaridade do SQLite, PK textual sem NOT NULL explícito merece validação na aplicação
url TEXT Não URL do anúncio
title TEXT Sim Título extraído
description TEXT Sim Descrição curta extraída; não é o HTML bruto completo
transaction_type TEXT Sim Tipo da transação; somente venda no snapshot
property_type TEXT Sim Tipo anunciado; apartamento ou casa
price_brl REAL Sim Preço anunciado em reais; sujeito a erros de escala
condominium_brl REAL Sim Condomínio anunciado; periodicidade não está formalizada
iptu_brl REAL Sim IPTU anunciado; periodicidade não está formalizada
area_m2 REAL Sim Uma única área em m², sem indicar se é útil, privativa, construída, total ou terreno
bedrooms INTEGER Sim Quartos anunciados, não a planta original comprovada
suites INTEGER Sim Suítes anunciadas
parking_spaces INTEGER Sim Vagas anunciadas
address TEXT Sim Texto de endereço; fortemente contaminado pela página na coleta atual
neighborhood TEXT Sim Bairro; fortemente contaminado pela página na coleta atual
city TEXT Sim Cidade; 100% nula atualmente
state TEXT Sim UF; 100% nula atualmente
advertiser_name TEXT Sim Nome do anunciante; 100% nulo atualmente
advertiser_code TEXT Sim Código do anunciante; parte relevante dos valores está contaminada
creci TEXT Sim Registro do anunciante; parte relevante dos valores está contaminada
latitude REAL Sim Latitude; 100% nula atualmente
longitude REAL Sim Longitude; 100% nula atualmente
published_at TEXT Sim Data publicada pelo portal; 100% nula atualmente
first_seen_at TEXT Não Primeira observação do anúncio
last_seen_at TEXT Não Última observação do anúncio
inactive_at TEXT Sim Momento de inativação inferida pelo coletor; 100% nulo atualmente
content_hash TEXT Sim SHA-256 do conteúdo normalizado usado para detectar mudança
raw_html_path TEXT Sim Caminho relativo para o HTML bruto do anúncio
extra_json TEXT Não '{}' JSON adicional; contém apenas json_ld, um array vazio nos 156 anúncios

Exemplo não sensível de identidade: o anúncio 1026522 aponta para uma URL sob www.dfimoveis.com.br e para um HTML em raw/listing/1026522/...html.gz.

3.3 listing_history

Representa estados distintos de um anúncio, não cada tentativa ou observação do coletor.

Coluna Tipo Nulo? Chave/default Interpretação
id INTEGER Não PK, AUTOINCREMENT Identificador do estado histórico; a PK inteira atribui um valor quando omitido
listing_id TEXT Não FK Anúncio ao qual o estado pertence
captured_at TEXT Não Momento em que o estado foi capturado
content_hash TEXT Não UNIQUE com listing_id Hash que impede repetir um estado idêntico para o anúncio
data_json TEXT Não Snapshot JSON dos principais campos extraídos

data_json é JSON válido em todas as 156 linhas. Ele contém os campos correntes de conteúdo, mas não contém first_seen_at, last_seen_at, inactive_at, raw_html_path ou uma referência à execução. Como a restrição é UNIQUE(listing_id, content_hash), observações repetidas sem mudança não geram novos snapshots.

3.4 listing_searches

Tabela de associação entre anúncio e consulta de busca que o encontrou.

Coluna Tipo Nulo? Chave/default Interpretação
listing_id TEXT Não PK parcial, FK Anúncio
search_url TEXT Não PK parcial URL/configuração da busca
first_seen_at TEXT Não Primeira observação nessa busca
last_seen_at TEXT Não Última observação nessa busca

A chave primária composta é (listing_id, search_url). Há duas URLs distintas no banco, uma para apartamentos de três quartos no Cruzeiro Novo e outra para casas anunciadas com três ou quatro quartos no Jardins Mangueiral.

3.5 photos

Representa arquivos de mídia associados diretamente ao anúncio. A tabela não preserva associação por snapshot.

Coluna Tipo Nulo? Chave/default Interpretação
listing_id TEXT Não PK parcial, FK Anúncio
ordinal INTEGER Não Ordem da imagem no anúncio
source_url TEXT Não PK parcial URL de origem da imagem
sha256 TEXT Sim Hash do arquivo baixado
mime_type TEXT Sim Tipo MIME detectado
bytes INTEGER Sim Tamanho do arquivo
local_path TEXT Sim Caminho relativo sob dfimoveis_data/
first_seen_at TEXT Não Primeira observação da mídia
last_seen_at TEXT Não Última observação da mídia

A chave primária é (listing_id, source_url), e não (listing_id, ordinal). Não há ordinais repetidos dentro de um anúncio na amostra atual. Exemplo de caminho: photos/1026522/001_9d7f11205f0a4a3f.jpg.

3.6 sqlite_sequence

Tabela interna do SQLite que mantém as sequências AUTOINCREMENT de crawl_runs e listing_history. Não é entidade do domínio.

4. Chaves, índices e integridade referencial

Índice Tabela Origem Colunas lógicas
sqlite_autoindex_listings_1 listings PK listing_id
sqlite_autoindex_listing_history_1 listing_history UNIQUE listing_id, content_hash
sqlite_autoindex_listing_searches_1 listing_searches PK listing_id, search_url
sqlite_autoindex_photos_1 photos PK listing_id, source_url

As FKs de listing_history, listing_searches e photos apontam para listings(listing_id), com NO ACTION para atualização e exclusão. Não há índices explícitos por data, preço, região ou hash de foto. Com o volume atual isso não é um problema operacional; se a base crescer, índices analíticos devem ser criados apenas em uma camada derivada ou recomendados para uma migração aprovada, nunca adicionados ao banco original durante análise.

5. Correspondência com o modelo conceitual

Conceito desejado Implementação física Avaliação
Fonte Ausente; DF Imóveis é implícito nas URLs Não permite múltiplas fontes com identidade segura
Execução de coleta crawl_runs Parcial; faltam fonte, configuração, versão e relação com itens
Anúncio listings Presente; estado corrente e identidade estão na mesma linha
Observação histórica listing_history Parcial; guarda somente conteúdo alterado, sem run_id
Imóvel físico candidato Ausente Nenhuma deduplicação física é persistida
Localização normalizada Colunas em listings Insuficiente e atualmente contaminada
Mídia photos Parcial; vinculada ao anúncio, não ao snapshot
Origem por consulta listing_searches Presente como relação anúncio–URL de busca

6. Decisões de interpretação e ambiguidades

  1. listing_id identifica um anúncio no portal, não um imóvel físico.
  2. Uma linha de listing_history é um estado de conteúdo distinto, não prova de que o anúncio foi observado somente uma vez. Observações idênticas são condensadas em last_seen_at.
  3. O banco ainda não contém histórico suficiente para liquidez: há quatro dias efetivos de observação e a maioria das transições é ruído de campos contaminados.
  4. Anúncios diferentes com preço, área ou fotos semelhantes permanecem anúncios distintos. Não há chave de endereço/unidade nem tabela de candidatos físicos.
  5. area_m2 não deve ser usada para preço por m² até que o conceito de área seja validado por tipologia e fonte.
  6. bedrooms é a quantidade anunciada. Ela não comprova a planta original de três quartos exigida para Jardins Mangueiral.
  7. Datas com sufixo +00:00 são tratadas como UTC. A cobertura observada vai de 13 a 21/07/2026.
  8. Os campos textuais contaminados devem ser reextraídos do HTML bruto em uma camada derivada; os valores atuais não devem ser silenciosamente normalizados como se fossem corretos.

7. Problemas conhecidos do snapshot

  • 150 de 161 endereços têm mais de 300 caracteres; 159 de 161 bairros têm mais de 100 caracteres.
  • city, state, latitude, longitude, published_at e advertiser_name estão integralmente ausentes.
  • 99 códigos de anunciante e 157 valores de CRECI são longos demais para o significado esperado, indicando extração contaminada.
  • Há um preço de R$ 570.000.000 e uma área de 0,11 m², ambos candidatos fortes a erro de extração/escala.
  • O JSON-LD reservado em extra_json é um array vazio em todos os 161 anúncios e não oferece uma fonte estruturada alternativa no snapshot.
  • As 7.363 linhas de foto têm hash e caminho local preenchidos; isso não garante que toda imagem pertença ao imóvel.
  • Hashes de foto idênticos aparecem em muitos anúncios; isso demonstra a presença de ativos genéricos e impede usar igualdade de hash como prova isolada de imóvel duplicado.
  • Das 401 transições históricas, 398 alteram creci; somente três anúncios apresentam mudança real de preço. O histórico precisa de normalização antes de análises temporais.
  • Não há como ligar formalmente anúncio/snapshot a crawl_runs, nem distinguir por chave a fonte se uma segunda fonte for incorporada.

12. Schema v2 — 22/07/2026

A migração foi autorizada explicitamente, executada após backup consistente e não removeu linhas ou colunas antigas.

Contagens preservadas na migração:

Tabela legada Linhas
listings 162
listing_searches 162
listing_history 637
photos 7.457
crawl_runs 21

Novas colunas:

  • listings: address_normalized, neighborhood_normalized, creci_normalized, raw_content_hash, stable_content_hash;
  • photos: discovery_source, is_primary_gallery, review_status;
  • crawl_runs: searches_json, config_json, scope_complete.

Novas tabelas:

  • availability_observations: evidência datada e sua fonte, sem converter selo visual em prova registral;
  • data_quality_issues: quarentenas reversíveis, com severidade, evidência e estado;
  • listing_change_events: eventos materiais produzidos pelo hash estável nas futuras coletas.

Novas visões:

  • v_primary_photos;
  • v_open_data_quality_issues;
  • v_listing_current_availability.

Auditoria inicial:

  • 3.521 fotos pertencem à galeria do próprio anúncio;
  • 3.286 registros legados apontam para /fotos/<outro_listing_id>/ e vieram de recomendações do portal;
  • 650 registros restantes são ativos visuais do site ou URLs sem proprietário de anúncio;
  • os 3.936 registros não primários foram preservados e marcados, não apagados;
  • 15 anúncios foram normalizados para o CRECI 26903;
  • PRAGMA quick_check retornou ok após a migração.