1// Copyright (C) MongoDB, Inc. 2017-present.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7package driver
8
9import (
10	"context"
11	"time"
12
13	"go.mongodb.org/mongo-driver/bson/bsoncodec"
14	"go.mongodb.org/mongo-driver/x/bsonx"
15
16	"go.mongodb.org/mongo-driver/mongo/options"
17	"go.mongodb.org/mongo-driver/x/mongo/driver/session"
18	"go.mongodb.org/mongo-driver/x/mongo/driver/topology"
19	"go.mongodb.org/mongo-driver/x/mongo/driver/uuid"
20	"go.mongodb.org/mongo-driver/x/network/command"
21	"go.mongodb.org/mongo-driver/x/network/description"
22)
23
24// Count handles the full cycle dispatch and execution of a count command against the provided
25// topology.
26func Count(
27	ctx context.Context,
28	cmd command.Count,
29	topo *topology.Topology,
30	selector description.ServerSelector,
31	clientID uuid.UUID,
32	pool *session.Pool,
33	registry *bsoncodec.Registry,
34	opts ...*options.CountOptions,
35) (int64, error) {
36
37	ss, err := topo.SelectServer(ctx, selector)
38	if err != nil {
39		return 0, err
40	}
41
42	desc := ss.Description()
43	conn, err := ss.Connection(ctx)
44	if err != nil {
45		return 0, err
46	}
47	defer conn.Close()
48
49	rp, err := getReadPrefBasedOnTransaction(cmd.ReadPref, cmd.Session)
50	if err != nil {
51		return 0, err
52	}
53	cmd.ReadPref = rp
54
55	// If no explicit session and deployment supports sessions, start implicit session.
56	if cmd.Session == nil && topo.SupportsSessions() {
57		cmd.Session, err = session.NewClientSession(pool, clientID, session.Implicit)
58		if err != nil {
59			return 0, err
60		}
61		defer cmd.Session.EndSession()
62	}
63
64	countOpts := options.MergeCountOptions(opts...)
65
66	if countOpts.Limit != nil {
67		cmd.Opts = append(cmd.Opts, bsonx.Elem{"limit", bsonx.Int64(*countOpts.Limit)})
68	}
69	if countOpts.MaxTime != nil {
70		cmd.Opts = append(cmd.Opts, bsonx.Elem{
71			"maxTimeMS", bsonx.Int64(int64(*countOpts.MaxTime / time.Millisecond)),
72		})
73	}
74	if countOpts.Skip != nil {
75		cmd.Opts = append(cmd.Opts, bsonx.Elem{"skip", bsonx.Int64(*countOpts.Skip)})
76	}
77	if countOpts.Collation != nil {
78		if desc.WireVersion.Max < 5 {
79			return 0, ErrCollation
80		}
81		collDoc, err := bsonx.ReadDoc(countOpts.Collation.ToDocument())
82		if err != nil {
83			return 0, err
84		}
85		cmd.Opts = append(cmd.Opts, bsonx.Elem{"collation", bsonx.Document(collDoc)})
86	}
87	if countOpts.Hint != nil {
88		hintElem, err := interfaceToElement("hint", countOpts.Hint, registry)
89		if err != nil {
90			return 0, err
91		}
92
93		cmd.Opts = append(cmd.Opts, hintElem)
94	}
95
96	return cmd.RoundTrip(ctx, desc, conn)
97}
98