Cogentcore

On July 24th, the first release of Cogent (https://www.cogentcore.org/), a new Go GUI cross-platform tool (Windows, OSX, Linux, Wasm, iOS, Android), was launched. This tool uses only Go language to build interfaces, with no templates or other files (like YAML, JSX), and the styling is done directly in the code. It follows the Material style.

What do I like about this project?

  • Clear goal
  • How to achieve the goal
  • The styling

What don’t I like?

  • I think that the best solution for GUI tools is a combination of declarative and imperative languages, like Qt (Qml + C++ or JavaScript), SwiftUI + Swift, and Slint, which also has an approach like this. Using this tool feels like going back to the time of Qt Widget.
  • For mobile, it uses Material Design, but I think that for mobile, it is essential to respect the native user interface.

For a first release, it looks good. I will keep it under observation and recommend you do the same.

Exploring Fyne: A Modern Cross-Platform Tool for Golang Developers

I don’t know if you’ve ever heard about Fyne (https://fyne.io/), the Golang cross-platform tool. I spent some hours trying it out. It runs on Windows, Linux, Darwin, Android, and iOS. The comparison with other cross-platform tools is inevitable, but it can also be unfair considering the age of the tool. With it, we can build very good desktop applications; more or less, we have all the fundamental GUI objects for a desktop application, and if you don’t need a Hollywood-style graphic, it’s good.

About the mobile situation, it’s a bit different. Mobile applications (apart from games) should conform to native guidelines, which is a bit hard with Fyne since it follows Material Design. What I’m saying is not a dismissal, but my advice is to use it if you need basic (or industrial) applications, where a nice user interface is less important than usual.

What’s my suggestion about it? If you need to build a desktop application (or mobile without a native look and feel), you already know Go, and you don’t want to spend time on other languages, then go for it. If you don’t like something, you can always contribute to the project—actions speak louder than words.

Note: English is not my native language, so I apologize for any errors. I use AI solely to generate the banner of the post; the content is human-generated.

MongoDb, Go and Docker Compose

Go Docker Mongodb

In this brief post, I’ll share how to use MongoDB in a Golang application executed within a Docker container, using docker-compose. The challenge is ensuring that the Golang application waits for the MongoDB instance to be fully operational before starting, to avoid connection retries.

To achieve this, create a docker-compose.yml file.

version: '3.9'

services:
api:
build: .
ports:
- 8080:8080
links:
- mongodb
depends_on:
- mongodb
environment:
WAIT_HOSTS: mongodb:27017

mongodb:
image: mongo:7.0
ports:
- 27017:27017
volumes:
- ~/apps/mongo:/data/db

From the Docker Compose file, we see that we have a service in the build directory that listens on port 8080. The ‘links’ property set to MongoDB means that the application can refer to the MongoDB service using the hostname ‘mongodb’. It also ‘depends on’ MongoDB, indicating that it must wait for MongoDB to start listening on port 27017 before initiating. In short, our service can only start once the MongoDB port is open.

Docker

FROM golang:1.21.3-alpine as builder
WORKDIR /root/
RUN apk add --update --no-cache make

RUN mkdir -p /root/mongo-docker-compose
WORKDIR /root/mongo-docker-compose
COPY . .
RUN make build

FROM alpine:3.18
WORKDIR /root/

COPY --from=builder /root/mongo-docker-compose/bin/ .

ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.8.0/wait wait
RUN chmod +x wait

ENV TESTDB_MONGO=mongodb

EXPOSE 8080
CMD ./wait && ./mongo-docker-compose

In the first part of the Dockerfile, the Golang application is built. In the second part, we prepare for its execution. More specifically, we download the ‘wait’ command and assign execution permissions to it. It’s small command-line utility to wait for other docker images to be started while using docker-compose. It permits waiting for a fixed amount of seconds, until a TCP port is open on a target image (our case) or until a file or directory is present on the local filesystem.

Continuing our examination of the Dockerfile, we observe that the ‘TESTDB_MONGO’ variable is also defined. This variable, utilized in the Go code, holds the ‘name’ of the MongoDB service as specified in the docker-compose file. If the definition of this variable is omitted, or if it contains an incorrect value, the application will be unable to connect to MongoDB, thereby rendering the application non-functional.

Code

Let’s now turn our attention to the Go code above. Here, we will explore how it interacts with the setup we’ve discussed, particularly in relation to the Docker and MongoDB configuration.

const (
	hostKey        = key("hostKey")
	UserCollection = "users"
	DBName         = "test"
	mongoKey       = "TESTDB_MONGO"
)

// Initialize the database

func init() {
	mongoServer := os.Getenv(mongoKey)
	if mongoServer == "" {
		mongoServer = "localhost"
	}
	ctx = context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	ctx = context.WithValue(ctx, hostKey, mongoServer)

	var err error
	testDb, err = configDB(ctx)

	if err != nil {
		fmt.Printf("db configuration %s\n", err)
	}
	fmt.Printf("db connected ")
}

It’s important to focus primarily on the ‘mongoKey’, which contains the value ‘TESTDB_MONGO’. This is the name of the environment variable that holds the MongoDB server’s name. That’s all. Enjoy with Go and Mongodb

It’s possible found the simple project here:

https://github.com/niqt/mongo-docker-compose

“Note: English is not my native language, so please excuse any errors. I would appreciate your assistance in correcting them.”

Golang MongoDb raw pagination

I’m writing a complex query (for a beginner user) for mongodb using the golang official driver, i’d like share my code to help other user.

I’ll use an aggragate to do the complex query, we need to step the first one to count the records of the query, the second step to get the elements at the request page.

var basePipeline = mongo.Pipeline{
 {{“$lookup”, bson.D{
 {“from”, “makers”},
 {“localField”, “maker”},
 {“foreignField”, “username”},
 {“as”, “makerinfo”},
 }}}}



The first piece fo the pipeline is the lookup, a similar left-join, we want join from the collection of products the makers. With the subpipeline we specify the conditions of the query:

var countPipeline mongo.Pipeline
var productsPipeline mongo.Pipeline
subpipeline := bson.D{
 {“$match”, bson.D{{“$and”,
 bson.A{
 bson.D{{“name”, bson.M{“$regex”: “.*” + initial + “.*”, “$options”: “-i”}}},
 bson.D{{“makers”, maker}},
 }}



I use two different pipeline, the countPipeline to count of the products, the second to gets the products. To have the count we must add other pipeline:

var countPipeline mongo.Pipeline
var productsPipeline mongo.Pipeline
subpipeline := bson.D{
        {"$match", bson.D{{"$and",
            bson.A{
                bson.D{{"name", bson.M{"$regex": ".*" + initial + ".*", "$options": "-i"}}},
                bson.D{{"makers", maker}},
        }}
countPipeline = append(basePipeline, subpipeline)
countCondition := bson.D{{"$count", "numberOfProduct"}}
countPipeline = append(countPipeline, countCondition)
productsPipeline = append(basePipeline, subpipeline)
 
cur, err = GetDB().Collection("products").Aggregate(GetCtx(), countPipeline)
// number of elements
var total int
if err != nil {
  fmt.Printf("[Debug] decode error %s", err)
  panic(err)
 } else {
  for cur.Next(GetCtx()) {
   dum := bson.M{"np": 0}
   if err = cur.Decode(&dum); err != nil {
    fmt.Printf("[Debug] decode error %s", err)
   }
   total = int(dum["numberOfProduct"].(int32))
  }
 }


Verify the page is in the range of the pages

// number of page
pages := total / 10
if (total % 10) > 0 {
  pages++
}
jumpPage, conError := strconv.Atoi(page)
if pages == 0 || conError != nil || jumpPage > pages {
  finalResult := bson.M{"products": result, "productsCount": 0, "pages": 0}
  return finalResult
}



Finally to gets the elements of the page:

result := []bson.M{}
var nProducts = 0
// get elements
jumpPagePipeline := bson.D{{"$skip", jumpPage}}
limitPipeline := bson.D{{"$limit", 10}}
productsPipeline = append(productsPipeline, jumpPagePipeline)
productsPipeline = append(productsPipeline, limitPipeline)
cur, err = GetDB().Collection("products").Aggregate(GetCtx(), productsPipeline)
// number of elements
if err != nil {
  fmt.Printf("[Debug] decode error %s", err)
  panic(err)
 } else {
  for cur.Next(GetCtx()) {
   dum := bson.M{}
   //var resultDecode bson.M
   if err = cur.Decode(&dum); err != nil {
    fmt.Printf("[Debug] decode error %s", err)
   }
   result = append(result, dum)
   nProducts++
  }
 }
finalResult := bson.M{"products": result, "productsCount": nProducts}



I used jumpedPipeline to jump to the page and limit to get only 10 elemets for page.

This example is not the better way to do the pagination, but it can be helpful to understand how build complex query.


For some better code you can see:

https://github.com/gobeam/mongo-go-pagination (it’s not my code)