Unable to sort by date in FindOne: GO Mongo driver

I am trying to get results sorted by datetime field using Golang mongo-driver, but I keep getting decoding error for the filed with the code below

type Source struct {
	Name            string
	Url             string
	Crawl           bool
	Failure         int64
	Success         int64
	Processed       primitive.DateTime `bson:"processed"`
	LastHomeRefresh primitive.DateTime `bson:"lastHomeRefresh"`
}

func GetOneSource(coll *mongo.Collection) (source Source, err error) {

	find := bson.D{{"crawl", true}}
	opts := options.FindOne().SetSort(bson.D{{"processed", 1}})
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	err = coll.FindOne(ctx, find, opts).Decode(&source)

	if err != nil {
		log.Print(err)
	}
	return
}

On calling the function I get

error decoding key processed: cannot decode string into a DateTime

I am able to get results when sorting with other fields without any issue, but using either of datetime fields returns this err. Explicitly adding bson data to struct also did not help

What am I missing here? Any help is appreciated.

Hi @Frank_Martin , thanks for the question! I’ve put together the following in attempts to reproduce your use case:

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type Source struct {
	Crawl     bool
	Processed primitive.DateTime `bson:"processed"`
}

func main() {
	opts := options.Client().ApplyURI("mongodb://localhost:27017")
	client, err := mongo.Connect(context.Background(), opts)
	if err != nil {
		panic(err)
	}

	defer func() { _ = client.Disconnect(context.Background()) }()

	// Insert some data to query.
	coll := client.Database("testdb").Collection("coll")
	coll.InsertOne(context.Background(), Source{
		Crawl:     true,
		Processed: primitive.NewDateTimeFromTime(time.Now()),
	})

	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	find := bson.D{{"crawl", true}}
	findopts := options.FindOne().SetSort(bson.D{{"processed", 1}})

	var source *Source
	err = coll.FindOne(ctx, find, findopts).Decode(&source)

	if err != nil {
		log.Print(err)
	}

	fmt.Println("processed: ", source.Processed)
}

The output is as I would expect:

pocessed:  1707260939662

The wire message that the Go Driver sends to the server is also as-expected:

{"find": "coll","filter": {"crawl": true},"limit": {"$numberLong":"1"},"singleBatch": true,"sort": {"processed": {"$numberInt":"1"}},"lsid": {"id": {"$binary":{"base64":"QRygb0e4QgivzS9BSeyAJw==","subType":"04"}}},"$clusterTime": {"clusterTime": {"$timestamp":{"t":1707261056,"i":1}},"signature": {"hash": {"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"00"}},"keyId": {"$numberLong":"0"}}},"$db": "testdb"}

I have a few questions

  1. Are you certain that the data you are querying has a datetime value for every source record’s processed field? You can check this with the following: db.<your coll>.aggregate([{ $group: { _id: { $type: "$processed" } } }]) . It’s worth noting that the server will sort data with string type before date and timestamp, you can see the specific rules here.

  2. If the processed data type is uniform, what version of the Go Driver are you seeing this behavior?

1 Like

Hi Preston_Vasquez, thanks for your reply.
I am sorry for being late to this, but I figured out the solution to this was setting the date type as

time.Time

instead of

primitive.DateTime

to make it work.

I can confirm that field had correct record for every field type. I did not looked into this issue more once I got it working.
I don’t know if the reason is that data is written by different process in JS and then called here which is causing the conflict, but that should not be the case.