【问题标题】:How can I add query parameters dynamically using gocql?如何使用 gocql 动态添加查询参数?
【发布时间】:2020-09-08 18:38:41
【问题描述】:

在一个函数(Golang、Gocql、Cassandra)中基于过滤器输入(vendor_id、customer_id、order_status_id ... 或所有非空字段)编写查询的最佳方法是什么?我不想在每种情况下都编写新函数,例如以下仅按 customerID 过滤的函数:

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
}

我需要给上面的查询动态添加参数

【问题讨论】:

  • 表结构: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);

标签: go cassandra gocql


【解决方案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
}

【讨论】:

  • 从编码的角度来看这可能很方便,但表实际上只能针对一种查询模式进行优化(分区)。其余的需要二级索引(反模式)。
  • 我的表索引是以这种方式创建的: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);
  • 这正是我的观点。添加索引在性能损失的情况下提供了查询便利......尤其是在规模上。
  • 为了在 Cassandra 中扩展,需要对表进行建模以适应(通常是一个)特定查询,并在它们之间复制数据。我在这里推荐的唯一二级索引是order_status_id(因为该值可能会发生变化),但即便如此,它也只能与分区键一起使用。 DataStax 提供免费在线课程 (DS220),详情请访问 academy.datastax.com
  • 我会考虑您的建议并对此进行更多研究。谢谢
猜你喜欢
  • 2019-11-06
  • 1970-01-01
  • 2019-12-30
  • 1970-01-01
  • 2021-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多