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.
 
 
 

14 KiB

Esquema físico do banco dfimoveis.sqlite3

1. Escopo e identificação do snapshot

Este documento descreve o esquema observado em 13 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 5.017.600 bytes
SHA-256 1ce3128b488c50c993d7328ac6e24b5905f083a7e04e3f636407eea1edd02b9d
Modificação do arquivo 2026-07-13 14:20:34.601285975 -03:00
SQLite 3.45.1
Codificação UTF-8
journal_mode persistido WAL
Páginas 1.225 páginas de 4.096 bytes; freelist_count = 0
user_version / application_id 0 / 0
Método de abertura sqlite3 -readonly e URI mode=ro&immutable=1, sempre com PRAGMA query_only = ON

Não havia arquivo -wal ou -shm nem processo com o banco aberto na verificação inicial. Isso permitiu tratar o conteúdo como snapshot imutável. PRAGMA quick_check retornou ok e PRAGMA foreign_key_check não retornou violações. Na validação final, o cliente SQLite havia criado os sidecars de runtime dfimoveis.sqlite3-shm (32.768 bytes) e dfimoveis.sqlite3-wal (vazio), comportamento possível ao abrir em leitura um banco configurado em WAL. Eles não foram removidos. O arquivo principal manteve tamanho, mtime e SHA-256 idênticos.

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 4 Execuções do coletor
listings 156 Identidades de anúncios e seu estado corrente
listing_history 156 Estados distintos dos anúncios por hash
listing_searches 156 Vínculos entre anúncio e URL de busca
photos 5.386 Mídias associadas diretamente aos anúncios
sqlite_sequence 2 Sequências internas de tabelas AUTOINCREMENT

No snapshot avaliado:

  • cada anúncio possui exatamente um registro em listing_history;
  • cada anúncio está ligado a exatamente uma URL de busca;
  • cada anúncio possui de 5 a 61 linhas em photos;
  • não há órfãos nas três relações com listings;
  • os 156 hashes de listing_history coincidem com o hash corrente em listings;
  • captured_at coincide com first_seen_at nos 156 anúncios.

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; somente ok na amostra
stats_json TEXT Não '{}' Contadores técnicos em JSON

As quatro estruturas JSON são válidas. As chaves presentes são search_pages, listing_urls, details_ok, details_failed, changed, photos_ok, photos_failed e inactive, todas inteiras.

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 preços ou permanência: há um snapshot por anúncio e todos os anúncios têm first_seen_at = last_seen_at.
  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 é de um único dia.
  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

  • 146 de 156 endereços têm mais de 300 caracteres; 154 de 156 bairros têm mais de 100 caracteres. Os dois bairros curtos também não formam valores confiáveis.
  • city, state, latitude, longitude, published_at e advertiser_name estão integralmente ausentes.
  • 98 códigos de anunciante e 153 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 anúncios e não oferece uma fonte estruturada alternativa no snapshot.
  • Três downloads de foto falharam e deixaram linhas sem hash, MIME, tamanho e caminho local.
  • Hashes de foto idênticos aparecem em muitos anúncios, inclusive um hash presente nos 156; isso demonstra a presença de ativos genéricos e impede usar igualdade de hash como prova isolada de imóvel duplicado.
  • Não há como ligar formalmente anúncio/snapshot a crawl_runs, nem distinguir por chave a fonte se uma segunda fonte for incorporada.