@stfed84

Какую выбрать схему хранения объектов?

Проект относится к сфере недвижимости.
Каждый объект недвижимости строго соответствует одному из типа: квартира, дом, гараж и т.д.
Каждый объект недвижимости обладает некими общими свойствами (адрес, площадь, цена и т.д.)
А есть свойства которые соответствуют только конкретному типу объекта (тип - дом: площадь участка, наличие бассейна и т.д.)

Сейчас у каждого типа объекта сформирована своя таблица в БД (MySQL).
Там перечислены как общие для всех объектов свойства так и уникальные.
В новой версии проекта планируется увеличение типов объектов.
Если работать по старой схеме то необходимо добавить много таблиц.
Много таблиц неудобно:
  • в каждой таблице свой ID
  • нельзя просто выбрать все объекты одного менеджера - необходимо делать последовательно выборки из нескольких таблиц
  • если вносить какой-то общий параметр необходимо вносить во множество таблиц
  • ...


Решением данных проблем видится создание одной общей таблицы объектов.
Сейчас встал вопрос о выборе схемы хранения данных:
Одна большая таблица
Тут в одной будут перечислены все возможные свойства объектов.
Причем поле площадь участка для объекта типа квартира не будет заполняться.
Вариант простой но избыточен.

Несколько таблиц
Таблица объектов, в ней будут только общие свойства
Таблица типов объектов: квартира, дом, участок...
Таблица всех свойств: кол-во комнат, жилая площадь, наличие мебели....
Таблица свойств типов будет показывать набор свойств соответствующий типу (один тип много свойств)
Таблица свойств объектов это значение свойств определенного объекта (один объект много свойств)
Вроде как правильный вариант, но стоит ли так всё усложнять?
Как и стоит ли реализовать хранение данных в соответствии с типом данных: int, varchar, enum, text ....
Или все значения записывать в виде text? - налицо потеря функциональности БД?
Как тогда сортировать объекты по какому-то свойству?

Прошу поделитесь своим опытом в реализации подобных проектов.
Может существуют еще какие-нибудь пути решения.

На сегодняшний момент:
Количество объектов более 5 тыс
Типов объектов 25
Свойств более 80
  • Вопрос задан
  • 341 просмотр
Пригласить эксперта
Ответы на вопрос 3
romy4
@romy4
Exception handler
Храните. например, каждое свойство - отдельная таблица со связкой + таблица агрегированных данных на каждый объект. Так вы получите выбор всех объектов по свойству + быструю отдачу данных. 5000 объектов — не та цифра, из-за которой заморачиваться по поводу занимаего места. А можно хранить связки не в каждой таблице, а в отдельной связывающей. В прочем, я не вижу, чтобы здесь была какая-то просадка по скорости даже на 80 джойнах, если везде связь по primary key.
Ответ написан
Rsa97
@Rsa97
Для правильного вопроса надо знать половину ответа
Собственно во втором варианте вы пришли к EAV-модели, она более универсальна, но при этом медленней классической, требует более сложных запросов и сложнее в контроле целостности.
Ответ написан
Комментировать
65536
@65536
на днях доделал такой сайт. ломание головы остановилось на eav с несколькими типами полей

вот как получилось
объявления
f1bc0bc3600979af86b4cfbf448642f6.png
категории объявлений
f7987ad4cb33f257c4b867e1d6841a14.png
общие поля для объяв из всех категорий
62102cd79000c00d74d4835868272352.png
свойства категорий
7d575bca3a46792ca52094fcbb294546.png
значения свойств для объявлений
58517b28450b090220a79838bd750712.png

основной косяк еава это то что невозможно выбрать одним запросом в зависимости от настроек фильтра, приходится по каждому параметру собирать айдишники и потом их интерсектить, примерно так
private function get_offers($page, $per_page)
{
    $filter_fields = $this->get_current_filter_enabled_fields();
    $filter_values = $this->s('@filter~:values_by_cats/' . $this->cat->id);

    //

    $offers_ids_sets = array();
    $offers_extra_ids_sets = array();

    if ($filter_values) {
        foreach ($filter_values as $field_id => $data) {
            if (isset($filter_fields[$field_id])) {
                $field = $filter_fields[$field_id];

                $prop = $field['prop'];
                $common_prop = $field['common_prop'];

                if ($prop) {
                    $type = $prop['type'];

                    if ($type == 'bool' && $data['value']) {
                        $offers_ids_sets[] = PropValue::where('prop_id', $prop['id'])
                                ->where('bool_value', true)
                                ->lists('offer_id')->toArray();
                    }

                    if ($type == 'strings_list' && isset($data['selected']) && !is_null($data['selected'])) {
                        $prop_settings = _j($prop['settings']);

                        if (isset($prop_settings['strings_list']['items'][$data['selected']])) {
                            $offers_ids_sets[] = PropValue::where('prop_id', $prop['id'])
                                    ->where('string_value', $prop_settings['strings_list']['items'][$data['selected']])
                                    ->lists('offer_id')->toArray();
                        }
                    }

                    if ($type == 'numbers_list' && isset($data['selected']) && !is_null($data['selected'])) {
                        $prop_settings = _j($prop['settings']);

                        if (isset($prop_settings['numbers_list']['items'][$data['selected']])) {
                            $offers_ids_sets[] = PropValue::where('prop_id', $prop['id'])
                                    ->where('number_value', $prop_settings['numbers_list']['items'][$data['selected']])
                                    ->lists('offer_id')->toArray();
                        }
                    }

                    if (($type == 'numbers_list' || $type == 'number') && isset($data['min']) && isset($data['max'])) {
                        $offers_ids_sets[] = PropValue::where('prop_id', $prop['id'])
                                ->where('number_value', '>=', $data['min'])
                                ->where('number_value', '<=', $data['max'])
                                ->lists('offer_id')->toArray();
                    }
                }

                if ($common_prop) {
                    $type = $common_prop['type'];

                    if (($type == 'numbers_list' || $type == 'number') && isset($data['min']) && isset($data['max'])) {
                        $offers_ids_sets[] = OfferExtra
                                ::where($common_prop['field'], '>=', $data['min'])
                                ->where($common_prop['field'], '<=', $data['max'])
                                ->lists('offer_id')->toArray();
                    }
                }
            }
        }
    }

    $offers_ids = array();

    if ($offers_ids_sets) {
        $offers_ids = (array)$offers_ids_sets[0];

        for ($i = 1; $i < count($offers_ids_sets); $i++) {
            $offers_ids = array_intersect($offers_ids, (array)$offers_ids_sets[$i]);
        }
    }

    //

    $builder = $this->cat->offers();

    if ($offers_ids_sets) {
        $builder = $builder->whereIn('id', $offers_ids);
    }

    $builder = $builder->whereHas('extra', function ($builder) use ($offers_extra_ids_sets) {
        $builder->where('enabled', true);
    });

    $count = $builder->count();

    $offers = $builder->orderBy('created_at', 'DESC')->offset(($page - 1) * $per_page)->take($per_page)->get();

    return array($count, $offers);
}

и вообще никак невозможно сортировать по параметрам, если нужна сортировка по какому-то параметру придется переносить ее в таблицу товаров самих

но все равно по гибкости он выигрывает у остальных вариантов. клиент сам через интерфейс может рулить своими категориями свойствами и тд. если бы была таблица в которой всё, она бы вешалась при каждой такой операции, на время пропорциональное кол-ву товаров
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы