MongoDB聚合查询与正在定义的结构不匹配

enyaitl3  于 2023-02-17  发布在  Go
关注(0)|答案(1)|浏览(123)

我的项目中有一个需求,我必须执行一个数据库操作来获取特定类型的用户总数。我所做的是过滤切片中的所有查询,并将Silce传递给我的数据库函数。
这是我调用DB函数的代码段

{

 filters =  []bson.D{
        {{Key: "Mykey", Value: myvalue}},
        {{Key: "Mykey", Value: myvalue}},
        {{Key: "Mykey", Value: myvalue}},
        {{Key: "Mykey", Value: myvalue}},

 counts, err := dbmain.NoOfDocumentsInfo(MyDBName, myCollectionName, filters...)
 
 }

下面是我调用的函数

func NoOfDocumentsInfo(DB string, col string, filters ...bson.D) ([]int64, error) {
if nil == dbInstance {
  if nil == GetDBInstance() {
    logger.Error("Not connecting to DB")
    err := errors.New("DB connection error")
    return nil, err
  }
    }

logger.Debugf("%s %s", DB, col)

coll := dbInstance.Database(DB).Collection(col)

counts := make([]int64, len(filters))
for i, filter := range filters {
  count, err := coll.CountDocuments(context.TODO(), filter)
  if err != nil {
    logger.Fatal(err)
    return nil, err
  }

  counts[i] = count
}

return counts, nil
}

正如您所看到的,我多次调用“科尔.CountDocuments”函数。我希望通过将所有筛选器聚合到单个查询中来编写代码,而不多次调用“coll.CountDocuments”函数。
我已尝试使用聚合管道,但“cur”和“result”输出为空。如果运行代码,您将能够看到它。

func NoOfDocumentsInfo(DB string, col string, filters ...bson.D) ([]int64, error) {
if dbInstance == nil {
  if GetDBInstance() == nil {
    logger.Error("Not connecting to DB")
    err := errors.New("DB connection error")
    return nil, err
  }
}

logger.Debugf("%s %s", DB, col)

coll := dbInstance.Database(DB).Collection(col)

pipeline := make([]bson.M, 0, len(filters)+2)
pipeline = append(pipeline, bson.M{"$match": bson.M{"$or": filters}})
pipeline = append(pipeline, bson.M{"$group": bson.M{"_id": nil, "count": bson.M{"$sum": 1}}})
pipeline = append(pipeline, bson.M{"$group": bson.M{"_id": nil, "count": bson.M{"$first": "$count"}}})

var result struct {
  Count int64 `bson:"count"`
}

cur, err := coll.Aggregate(context.TODO(), pipeline)
if err != nil {
  logger.Fatal(err)
  return nil, err
}

logger.Debugf("cur: %+v", cur)
err = cur.Decode(&result)
logger.Debugf("result: %+v, err: %v", result, err)
if err != nil {
logger.Fatal(err)
return nil, err
}

return []int64{result.Count}, nil
}
olmpazwi

olmpazwi1#

你必须为$group中的每个过滤器添加一个字段,你可以使用$cond来有条件地递增给定的计数器,但是这很可能最终不使用索引,因此甚至比单独的原始计数查询。另请注意,使用$or也可能导致跳过索引。另请注意,在$cond中,您可能必须转换筛选器(例如,将$添加到字段名称中)。
最好为每个过滤器启动并发计数查询(使用go),如果它们被索引,它们将快速完成。

func docCounts(db string, col string, filters ...bson.D) ([]int64, error) {
    // ... obtain collection
    coll := dbInstance.Database(db).Collection(col)

    counts := make([]int64, len(filters))
    errs := make([]error, len(filters))

    wg := &sync.WaitGroup{}
    wg.Add(len(filters))

    for i := range filters {
        go func(i int) {
            defer wg.Done()
            counts[i], errs[i] = coll.CountDocuments(context.TODO(), filters[i])
        }(i)
    }

    wg.Wait()

    // Produce some kind of error if any of the queries failed.
    var err error
    for _, e := range errs {
        if e != nil {
            err = fmt.Errorf("at least one query failed: %w", e)
            break
        }
    }
    // Note: starting with Go 1.20, you could simply write:
    // err = errors.Join(errs)

    return counts, err
}

相关问题