FAQ Module Development for 1C-Bitrix
FAQ on Bitrix is most commonly built with an infoblock containing two fields — question and answer — and the bitrix:catalog.list component in a simplified version. Problems begin when there are more than 50 questions: no full-text search across answers, no section grouping with accordion, no analytics on which questions are viewed most often, no way to ask a question directly from the FAQ page. A specialized module solves each of these problems.
Data Model
Module vendor.faq:
-
b_vendor_faq_category— categories: id, name, slug, sort, is_active, questions_count (denormalized counter) -
b_vendor_faq_question— questions: id, category_id, question, answer (HTML), slug, sort, views, is_active, is_featured, created_at, updated_at -
b_vendor_faq_vote— "helped/didn't help" votes: id, question_id, user_id, ip, value, created_at -
b_vendor_faq_search_log— search log: id, query, results_count, created_at
Full-Text Search
Bitrix's standard search via bitrix:search.title indexes pages, not records. FAQ needs instant search across questions and answers without page reload:
// AJAX search endpoint
public function search(string $query): array
{
$query = mb_strtolower(trim($query));
// Log the query for analytics
SearchLogTable::add(['QUERY' => $query]);
$result = QuestionTable::getList([
'select' => ['ID', 'QUESTION', 'ANSWER', 'CATEGORY_ID'],
'filter' => [
'IS_ACTIVE' => 'Y',
[
'LOGIC' => 'OR',
'%QUESTION' => $query,
'%ANSWER' => $query,
],
],
'limit' => 10,
'order' => ['IS_FEATURED' => 'DESC', 'VIEWS' => 'DESC'],
])->fetchAll();
// Highlight matches in results
return array_map(fn($q) => $this->highlight($q, $query), $result);
}
For large databases (1000+ questions), %LIKE% search in PostgreSQL is replaced with a full-text index (tsvector). The fts_vector column is updated by a trigger when a question or answer is changed.
View Counter and Rating
When a question is opened, the counter is incremented via AJAX:
QuestionTable::update($questionId, ['VIEWS' => new \Bitrix\Main\DB\SqlExpression('VIEWS + 1')]);
After reading, the user can rate the answer: "This helped" / "Didn't help". Vote statistics are used for two purposes: sorting (helpful questions rank higher) and identifying problematic answers (many negatives — needs rewriting).
Accordion Structure
The vendor:faq.list component renders questions grouped by category. The accordion is implemented in CSS (:details/:summary) without JavaScript — works without JS and indexes cleanly. When the GET parameter ?q=slug-of-question is present, the relevant section is expanded automatically via the open attribute.
"Ask a Question" Form
If the user does not find an answer in the search, the form invites them to ask a question. The question is saved to b_vendor_feedback_submission (integration with the feedback module) or directly to b_vendor_faq_question with a draft status for later publication. The administrator can publish it in the FAQ if the question is of broad interest.
Schema.org FAQPage
Microdata is important for search visibility:
<div itemscope itemtype="https://schema.org/FAQPage">
<div itemscope itemprop="mainEntity" itemtype="https://schema.org/Question">
<h3 itemprop="name">How do I process a return?</h3>
<div itemscope itemprop="acceptedAnswer" itemtype="https://schema.org/Answer">
<div itemprop="text">To process a return...</div>
</div>
</div>
</div>
The component automatically generates microdata for all published questions on the page.
Analytics
A report is built from b_vendor_faq_search_log:
- Top search queries with no results — what users look for but don't find
- Popular questions by views over a period
- Questions with a high "didn't help" percentage
This data helps editors understand which questions to add and which answers to rewrite.
Development Timeline
| Stage | Duration |
|---|---|
| ORM tables, category and question model | 0.5 days |
| AJAX search with highlighting | 1 day |
| Accordion component, Schema.org | 1 day |
| View counters, voting | 0.5 days |
| "Ask a question" form | 0.5 days |
| Analytics and reports | 1 day |
| Admin interface | 1 day |
| Testing | 0.5 days |
Total: 6 working days. Connecting full-text search via PostgreSQL tsvector — +1 day.







