Semantic Search in DDC CWICR Database
Business Case
Problem Statement
Construction cost estimation requires finding relevant work items from large databases. Traditional keyword search fails when:
- Users describe work in natural language
- Terminology varies across regions and languages
- Similar work items have different naming conventions
Solution
DDC CWICR provides pre-computed embeddings (BAAI/bge-m3, 1024 dimensions) enabling multilingual semantic search across 8 national bases (78,228 positions) plus the 30-market global base in 26 languages, with 48 PPP-repriced market catalogs per national base.
Business Value
- 90% faster work item lookup compared to manual search
- Multi-language: Arabic, Bulgarian, Chinese, Croatian, Czech, Danish, Dutch, English, Finnish, French, German, Hindi, Indonesian, Italian, Japanese, Korean, Mongolian, Norwegian, Polish, Portuguese, Romanian, Russian, Spanish, Swedish, Thai, Turkish, Vietnamese
- Higher accuracy by finding semantically similar items, not just keyword matches
Data landscape (2026)
| National base |
Region id |
Positions |
| Turkey (Birim Fiyat) |
TR_NATIONAL |
22,704 |
| China (Beijing Dinge + Bole) |
ZH_CHINA |
11,312 |
| Brazil (SINAPI) |
BR_NATIONAL |
9,723 |
| Spain (BCCA Andalucía) |
ES_ANDALUCIA |
6,453 |
| Italy (Prezzario Toscana) |
IT_TOSCANA |
5,836 |
| Vietnam (Dinh Muc) |
VN_NATIONAL |
4,299 |
| Indonesia (AHSP) |
ID_NATIONAL |
2,784 |
| Greece (GGDE) |
GR_NATIONAL |
2,647 |
Each base ships the 95-column CWICR master schema (rate_code, rate_original_name, rate_final_name, rate_unit, total_cost_per_position, classification hierarchy collection/department/section/subsection/category, resource_* component lines with is_material/is_machine/is_labor flags) plus 26 language editions and 48 markets/*.csv catalogs.
Latest data release: v0.4.0 (see releases).
Technical Implementation
Prerequisites
pip install qdrant-client pandas sentence-transformers
Collections (2026)
The vector store uses BAAI/bge-m3 (1024-dim dense + sparse + colbert in one forward pass, MIT license, 100+ languages). Production collections are named cwicr_{LANG}_v3 (e.g. cwicr_tr_v3, cwicr_zh_v3). An ONNX-int8 variant (gpahal/bge-m3-onnx-int8, ~700 MB) is used on VPS-sized hosts.
Python Implementation
import pandas as pd
from qdrant_client import QdrantClient
from sentence_transformers import SentenceTransformer
class CWICRSemanticSearch:
def __init__(self, host="localhost", port=6333, lang="en"):
self.client = QdrantClient(host=host, port=port)
self.collection = f"cwicr_{lang}_v3"
self.model = SentenceTransformer("BAAI/bge-m3")
def search_work_items(self, query, limit=10):
vec = self.model.encode(query).tolist()
hits = self.client.search(
collection_name=self.collection,
query_vector=vec,
limit=limit,
)
return pd.DataFrame([{**h.payload, "score": h.score} for h in hits])
def search_by_category(self, query, category, limit=10):
vec = self.model.encode(query).tolist()
hits = self.client.search(
collection_name=self.collection,
query_vector=vec,
query_filter={"must": [{"key": "category", "match": {"value": category}}]},
limit=limit,
)
return pd.DataFrame([{**h.payload, "score": h.score} for h in hits])
Inside OpenConstructionERP
The platform's costs module already exposes semantic matching:
POST /api/v1/costs/suggest-for-element — rank cost items for a BIM element body.
/qdrant-search — multilingual candidate retrieval for a query.
- The SQL fallback (
GET /api/v1/costs/?q=...) works without Qdrant.
Database Schema (95-column master)
Key fields the payload carries:
| Field |
Type |
Description |
rate_code |
string |
Unique work item code (e.g. 15.115.1008) |
rate_original_name |
string |
Source-language description |
rate_final_name |
string |
Display/translated description |
rate_unit |
string |
m², m³, m, kg, Ad, Sa… |
total_cost_per_position |
float |
Total unit price |
total_resource_cost_per_position |
float |
Resource sum (before markup) |
collection_name / department_name / section_name / subsection_name |
string |
Classification hierarchy |
category_type |
string |
Normalized category (e.g. CONSTRUCTION WORK) |
resource_name / resource_quantity / resource_price_per_unit_current / resource_cost |
mixed |
Component lines |
is_material / is_machine / is_labor |
bool |
Component nature flags |
Usage Examples
Basic Search
search = CWICRSemanticSearch(lang="tr")
# Natural language query
results = search.search_work_items("tuğla duvar örülmesi")
print(results[["rate_code", "rate_original_name", "total_cost_per_position", "score"]])
Cost Estimation
# Find work items for foundation work
foundation = search.search_work_items("reinforced concrete foundation", limit=20)
# Estimate with quantities (BIM takeoff)
quantities = {"15.115.1008": 150.0} # m³
total = sum(quantities[c] * row["total_cost_per_position"]
for _, row in foundation.iterrows() if row["rate_code"] in quantities)
print(f"Estimated: {total:,.2f} TRY")
Best Practices
- Use specific queries - "reinforced concrete slab 200mm" beats "concrete"
- Filter by category - Narrow results to relevant work types
- Check similarity scores - Low scores need manual verification
- Combine with QTO - Use BIM quantities for automated estimation
- Mind the coefficient bases - Vietnam and Indonesia have no prices (rate 0); price them via a market resource sheet
- Trust the source column -
rate_original_name holds the source wording; translations live in rate_final_name
Resources
1---2name: semantic-search-cwicr3description: Semantic search in the DDC CWICR construction cost database using vector embeddings (BGE-M3, 1024-dim, per-language Qdrant collections). Find similar work items and resources for cost estimation across 8 national bases and 30 markets in 26 languages.4---5
6# Semantic Search in DDC CWICR Database
7
8## Business Case
9
10### Problem Statement
11Construction cost estimation requires finding relevant work items from large databases. Traditional keyword search fails when:
12- Users describe work in natural language
13- Terminology varies across regions and languages
14- Similar work items have different naming conventions
15
16### Solution
17DDC CWICR provides pre-computed embeddings (BAAI/bge-m3, 1024 dimensions) enabling multilingual semantic search across **8 national bases (78,228 positions)** plus the **30-market global base** in **26 languages**, with 48 PPP-repriced market catalogs per national base.
18
19### Business Value
20- **90% faster** work item lookup compared to manual search
21- **Multi-language**: Arabic, Bulgarian, Chinese, Croatian, Czech, Danish, Dutch, English, Finnish, French, German, Hindi, Indonesian, Italian, Japanese, Korean, Mongolian, Norwegian, Polish, Portuguese, Romanian, Russian, Spanish, Swedish, Thai, Turkish, Vietnamese
22- **Higher accuracy** by finding semantically similar items, not just keyword matches
23
24## Data landscape (2026)
25
26| National base | Region id | Positions |
27|---|---|---|
28| Turkey (Birim Fiyat) | `TR_NATIONAL` | 22,704 |
29| China (Beijing Dinge + Bole) | `ZH_CHINA` | 11,312 |
30| Brazil (SINAPI) | `BR_NATIONAL` | 9,723 |
31| Spain (BCCA Andalucía) | `ES_ANDALUCIA` | 6,453 |
32| Italy (Prezzario Toscana) | `IT_TOSCANA` | 5,836 |
33| Vietnam (Dinh Muc) | `VN_NATIONAL` | 4,299 |
34| Indonesia (AHSP) | `ID_NATIONAL` | 2,784 |
35| Greece (GGDE) | `GR_NATIONAL` | 2,647 |
36
37Each base ships the **95-column CWICR master schema** (`rate_code`, `rate_original_name`, `rate_final_name`, `rate_unit`, `total_cost_per_position`, classification hierarchy `collection/department/section/subsection/category`, `resource_*` component lines with `is_material/is_machine/is_labor` flags) plus 26 language editions and 48 `markets/*.csv` catalogs.
38
39Latest data release: **v0.4.0** (see [releases](https://github.com/datadrivenconstruction/OpenConstructionEstimate-DDC-CWICR/releases)).
40
41## Technical Implementation
42
43### Prerequisites
44```bash
45pip install qdrant-client pandas sentence-transformers
46```
47
48### Collections (2026)
49
50The vector store uses **BAAI/bge-m3** (1024-dim dense + sparse + colbert in one forward pass, MIT license, 100+ languages). Production collections are named `cwicr_{LANG}_v3` (e.g. `cwicr_tr_v3`, `cwicr_zh_v3`). An ONNX-int8 variant (`gpahal/bge-m3-onnx-int8`, ~700 MB) is used on VPS-sized hosts.
51
52### Python Implementation
53
54```python
55import pandas as pd
56from qdrant_client import QdrantClient
57from sentence_transformers import SentenceTransformer
58
59class CWICRSemanticSearch:
60 def __init__(self, host="localhost", port=6333, lang="en"):
61 self.client = QdrantClient(host=host, port=port)
62 self.collection = f"cwicr_{lang}_v3"
63 self.model = SentenceTransformer("BAAI/bge-m3")
64
65 def search_work_items(self, query, limit=10):
66 vec = self.model.encode(query).tolist()
67 hits = self.client.search(
68 collection_name=self.collection,
69 query_vector=vec,
70 limit=limit,
71 )
72 return pd.DataFrame([{**h.payload, "score": h.score} for h in hits])
73
74 def search_by_category(self, query, category, limit=10):
75 vec = self.model.encode(query).tolist()
76 hits = self.client.search(
77 collection_name=self.collection,
78 query_vector=vec,
79 query_filter={"must": [{"key": "category", "match": {"value": category}}]},
80 limit=limit,
81 )
82 return pd.DataFrame([{**h.payload, "score": h.score} for h in hits])
83```
84
85### Inside OpenConstructionERP
86
87The platform's `costs` module already exposes semantic matching:
88- `POST /api/v1/costs/suggest-for-element` — rank cost items for a BIM element body.
89- `/qdrant-search` — multilingual candidate retrieval for a query.
90- The SQL fallback (`GET /api/v1/costs/?q=...`) works without Qdrant.
91
92## Database Schema (95-column master)
93
94Key fields the payload carries:
95
96| Field | Type | Description |
97|-------|------|-------------|
98| `rate_code` | string | Unique work item code (e.g. `15.115.1008`) |
99| `rate_original_name` | string | Source-language description |
100| `rate_final_name` | string | Display/translated description |
101| `rate_unit` | string | m², m³, m, kg, Ad, Sa… |
102| `total_cost_per_position` | float | Total unit price |
103| `total_resource_cost_per_position` | float | Resource sum (before markup) |
104| `collection_name` / `department_name` / `section_name` / `subsection_name` | string | Classification hierarchy |
105| `category_type` | string | Normalized category (e.g. `CONSTRUCTION WORK`) |
106| `resource_name` / `resource_quantity` / `resource_price_per_unit_current` / `resource_cost` | mixed | Component lines |
107| `is_material` / `is_machine` / `is_labor` | bool | Component nature flags |
108
109## Usage Examples
110
111### Basic Search
112```python
113search = CWICRSemanticSearch(lang="tr")
114
115# Natural language query
116results = search.search_work_items("tuğla duvar örülmesi")
117print(results[["rate_code", "rate_original_name", "total_cost_per_position", "score"]])
118```
119
120### Cost Estimation
121```python
122# Find work items for foundation work
123foundation = search.search_work_items("reinforced concrete foundation", limit=20)
124
125# Estimate with quantities (BIM takeoff)
126quantities = {"15.115.1008": 150.0} # m³
127total = sum(quantities[c] * row["total_cost_per_position"]
128 for _, row in foundation.iterrows() if row["rate_code"] in quantities)
129print(f"Estimated: {total:,.2f} TRY")
130```
131
132## Best Practices
133
1341. **Use specific queries** - "reinforced concrete slab 200mm" beats "concrete"
1352. **Filter by category** - Narrow results to relevant work types
1363. **Check similarity scores** - Low scores need manual verification
1374. **Combine with QTO** - Use BIM quantities for automated estimation
1385. **Mind the coefficient bases** - Vietnam and Indonesia have no prices (rate 0); price them via a market resource sheet
1396. **Trust the source column** - `rate_original_name` holds the source wording; translations live in `rate_final_name`
140
141## Resources
142
143- **GitHub**: [OpenConstructionEstimate-DDC-CWICR](https://github.com/datadrivenconstruction/OpenConstructionEstimate-DDC-CWICR)
144- **Releases**: [v0.4.0](https://github.com/datadrivenconstruction/OpenConstructionEstimate-DDC-CWICR/releases)
145- **Platform**: [OpenConstructionERP](https://github.com/datadrivenconstruction/OpenConstructionERP)
146- **Qdrant Docs**: https://qdrant.tech/documentation/