Как я могу динамически добавлять параметры запроса с помощью gocql?

Как лучше всего написать запрос на основе входных данных фильтра (vendor_id, customer_id, order_status_id ... или все поля nonempt одновременно) в одной функции (Golang, Gocql, Cassandra)? Я не хочу писать новую функцию в каждом случае, как приведенная ниже функция, которая фильтрует только по идентификатору клиента:

func (repo *orderRepo) FindByCustomerId(req *pb.FindRequest) (*pb.OrdersResponse, error) {
    offset := req.GetOffset()
    limit := req.GetLimit()
    customerID := req.GetCustomerId()
    //vendorID := req.GetVendorId()
    //orderStatusID := req.GetOrderStatusId()
    var (
        orders []*pb.Order
        count  int32
    )
    if err := repo.session.Query(`SELECT count(1) FROM orders WHERE customer_id = ?`, customerID).
        Scan(&count); err != nil {
        return nil, err
    }
    if offset < count {
        query := repo.session.Query(`SELECT
            id,
            customer_id,
            customer_name,
            vendor_id,
            toJSON(items),
            paid_at,
            created_at,
            order_status_id
        FROM orders WHERE customer_id = ?`, customerID)
        defer query.Release()
        var ps []byte
        iter := query.PageState(ps).PageSize(int(offset % limit)).Iter()
        ps = iter.PageState()
        page := offset / limit
        var i int32
        for i = 0; i < page; i++ {
            iter = query.PageState(ps).PageSize(int(limit)).Iter()
            ps = iter.PageState()
        }
        iter = query.PageState(ps).PageSize(int(limit)).Iter()
        rows := iter.Scanner()
        for rows.Next() {
            var (
                order                pb.Order
                createdAt            time.Time
                paidAt               time.Time
                itemsJSON            []byte
            )
            if err := rows.Scan(
                &order.Id,
                &order.CustomerId,
                &order.CustomerName,
                &order.VendorId,
                &itemsJSON,
                &paidAt,
                &createdAt,
                &order.OrderStatusId,
            ); err != nil {
                return nil, err
            }
            if err := json.Unmarshal(itemsJSON, &order.Items); err != nil {
                return nil, err
            }
            order.PaidAt = paidAt.String()
            order.CreatedAt = createdAt.String()
            orders = append(orders, &order)
        }

        if err := iter.Close(); err != nil {
            return nil, err
        }
    }

    return &pb.OrdersResponse{
        Orders: orders,
        Count:  count,
    }, nil
}

Мне нужно динамически добавлять параметры к вышеуказанному запросу


person Saidamir Botirov    schedule 04.09.2020    source источник
comment
Структура таблицы: CREATE TABLE IF NOT EXISTS orders (... primary key (vendor_id, year, number) ) WITH CLUSTERING ORDER BY (year DESC, number DESC); CREATE INDEX ON orders(id); CREATE INDEX ON orders(order_status_id); CREATE INDEX ON orders(customer_id);   -  person Saidamir Botirov    schedule 09.09.2020


Ответы (1)


Вот мое решение собственного вопроса, который у меня возник за 3 дня :) Думаю, это поможет другим)

func (repo *orderRepo) Find(req *pb.FindRequest) (*pb.OrdersResponse, error) {
    offset := req.GetOffset()
    limit := req.GetLimit()
    vendorID := req.GetVendorId()
    customerID := req.GetCustomerId()
    orderStatusID := req.GetOrderStatusId()

    var (
        orders []*pb.Order
        count  int32
    )

    var filter string
    var values []interface{}
    if len(vendorID) > 0 {
        if len(filter) > 0 {
            filter = filter + " AND vendor_id = ?"
        } else {
            filter = " WHERE vendor_id = ?"
        }
        values = append(values, vendorID)
    }
    if len(customerID) > 0 {
        if len(filter) > 0 {
            filter = filter + " AND customer_id = ?"
        } else {
            filter = " WHERE customer_id = ?"
        }
        values = append(values, customerID)

    }
    if len(orderStatusID) > 0 {
        if len(filter) > 0 {
            filter = filter + " AND order_status_id = ?"
        } else {
            filter = " WHERE order_status_id = ?"
        }
        values = append(values, orderStatusID)
    }

    fmt.Println(filter)

    if err := repo.session.Query(`SELECT count(1) FROM orders`+filter, values...).
        Scan(&count); err != nil {
        return nil, err
    }

    if offset < count {
        query := repo.session.Query(`SELECT
        id,
        customer_id,
        customer_name,
        vendor_id,
        toJSON(items),
        paid_at,
        created_at,
        order_status_id
        FROM orders`+filter, values...)
        defer query.Release()

        var ps []byte
        iter := query.PageState(ps).PageSize(int(offset % limit)).Iter()
        ps = iter.PageState()

        page := offset / limit
        var i int32
        for i = 0; i < page; i++ {
            iter = query.PageState(ps).PageSize(int(limit)).Iter()
            ps = iter.PageState()
        }
        iter = query.PageState(ps).PageSize(int(limit)).Iter()

        rows := iter.Scanner()
        for rows.Next() {
            var (
                order                pb.Order
                createdAt            time.Time
                paidAt               time.Time
                itemsJSON            []byte
            )
            if err := rows.Scan(
                &order.Id,
                &order.CustomerId,
                &order.CustomerName,
                &order.VendorId,
                &itemsJSON,
                &paidAt,
                &createdAt,
                &order.OrderStatusId,
            ); err != nil {
                return nil, err
            }
            if err := json.Unmarshal(itemsJSON, &order.Items); err != nil {
                return nil, err
            }
            order.PaidAt = paidAt.String()
            order.CreatedAt = createdAt.String()
            orders = append(orders, &order)
        }

        if err := iter.Close(); err != nil {
            return nil, err
        }
    }

    return &pb.OrdersResponse{
        Orders: orders,
        Count:  count,
    }, nil
}
person Saidamir Botirov    schedule 08.09.2020
comment
Это может быть удобно с точки зрения кодирования, но на самом деле таблица может быть оптимизирована (секционирована) только для одного шаблона запроса. Для остальных потребуется вторичный индекс (антипаттерн). - person Aaron; 08.09.2020
comment
Индекс моей таблицы создан таким образом: CREATE TABLE IF NOT EXISTS orders (... primary key (vendor_id, year, number) ) WITH CLUSTERING ORDER BY (year DESC, number DESC); CREATE INDEX ON orders(id); CREATE INDEX ON orders(order_status_id); CREATE INDEX ON orders(customer_id); - person Saidamir Botirov; 09.09.2020
comment
Это как раз моя точка зрения. Добавление индексов обеспечивает удобство запросов при потере производительности ... особенно при масштабировании. - person Aaron; 09.09.2020
comment
Мне нужно выполнить фильтрацию на основе этих полей [(vendor_id), (customer_id), (order_status_id), (vendor_id, cusomter_id), (vendor_id, order_status_id)], что вы можете порекомендовать для хорошей производительности в масштабе в моем случае? - person Saidamir Botirov; 10.09.2020
comment
Чтобы масштабировать в Cassandra, ваши таблицы должны быть смоделированы в соответствии с (обычно одним) конкретным запросом и дублировать ваши данные в них. Единственный вторичный индекс, который я бы порекомендовал здесь, - это order_status_id (поскольку это значение, вероятно, изменится), но даже в этом случае его следует использовать только с ключом раздела. DataStax предлагает бесплатный онлайн-курс (DS220), который более подробно рассматривается на academy.datastax.com. - person Aaron; 10.09.2020
comment
Я рассматриваю вашу рекомендацию и исследую ее подробнее. Спасибо - person Saidamir Botirov; 10.09.2020