How to append to bson object - mongodb

I have an endpoint where users can filter a mongo collection using query parameters. If I have just one query parameter e.g. title, I can do this -
filter := bson.M{}
if params.Title != "" {
filter = bson.M{"title": params.Title}
}
However, if I have more than one query parameter, I can't seem to get how to append to the bson object.
I tried this -
filter := []bson.M{}
if params.Title != "" {
filter = append(filter, bson.M{"title": params.Title})
}
if params.Description != "" {
filter = append(filter, bson.M{"description": params.Description})
}
but I got this error - cannot transform type []primitive.M to a BSON Document: WriteArray can only write a Array while positioned on a Element or Value but is positioned on a TopLevel
How do I solve this?

bson.M{} is underlined map[string]interface{} in go-mongo-driver. So if you need to add more elemnets, you can not append. Just assign that value to map's key as below.
filter := bson.M{}
if params.Title != "" {
//filter = bson.M{"title": params.Title}
filter["title"] = params.Title
}
if params.Description != "" {
filter["description"] = params.Description
}

Consider a collection test with a document: { "_id" : 1, "Title" : "t-1", "Description" : "d-1" }
And, you can use the following:
title := "t-1"
description := "" // or "d-1"
filter := bson.M{}
if Title != "" {
filter["Title"] = title
}
if Description != "" {
filter["Description"] = description
}
//fmt.Println(filter);
var result bson.M
collection := client.Database("test").Collection("test")
err := collection.FindOne(context.TODO(), filter).Decode(&result)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found a single document: %+v\n", result)

Related

Returning more than one value in filter using regex

I have a JSON file that follows a format like this for each record containing the names of various cities across the world.
{
"_id": {
"$oid": "5f856f2ee2d00c1818791982"
},
"name": "Sant Julia de Loria",
"all_names": "Sant Julià de Lòria, Sant Julia de Loria, San Julia,San Julià,Sant Julia de Loria,Sant Julià de Lòria,Sant-Zhulija-de-Lorija,sheng hu li ya-de luo li ya,Сант-Жулия-де-Лория,サン・ジュリア・デ・ロリア教区,圣胡利娅-德洛里亚,圣胡利娅-德洛里亚",
"alternate_names": "San Julia,San Julià,Sant Julia de Loria,Sant Julià de Lòria,Sant-Zhulija-de-Lorija,sheng hu li ya-de luo li ya,Сант-Жулия-де-Лория,サン・ジュリア・デ・ロリア教区,圣胡利娅-德洛里亚,圣胡利娅-德洛里亚",
"country_code": "AD",
"country_name": "Andorra"
}
My app basically returns the name of the city when querying it through a URL parameter. This is supposed to pass through a regex filter and then searches the JSON for the appropriate city name.
func searchCity(w http.ResponseWriter, r *http.Request) { // GET request that takes in a url param and returns a list of cities from the DB
w.Header().Set("Content-Type", "application/json")
values := r.URL.Query()
city := values["city_name"] // Assigns url param from route `/suggest?city_name=` to 'city'
cityCollection := client.Database(os.Getenv("CITY_DB")).Collection(os.Getenv("COL_CITY")) // Holds 'city' collection from DB
if len(city) == 0 {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"Error": "City name cannot be empty!"}`))
return
}
filter := bson.D{ //Regex filter for querying the input through the DB
primitive.E{
Key: "all_names", Value: primitive.Regex{
Pattern: city[0], Options: "i",
},
},
}
cursor, err := cityCollection.Find(r.Context(), filter) // Query to find city is declared with cursor
if err != nil {
log.Fatal(err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(`{"Error": "Could not execute cursor into query."}`))
return
}
var cityList []City
for cursor.Next(context.TODO()) { // Runs through each document entry in DB to see if regex matches
var cities City
err := cursor.Decode(&cities)
if err != nil {
log.Fatal(err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(`{"Error": "Could not run cursor."}`))
return
}
cityList = append(cityList, cities) // List of cities is appended to cityList
}
w.WriteHeader(http.StatusOK)
if len(cityList) == 0 {
w.Write([]byte(`{"cities": []}`))
return
}
json.NewEncoder(w).Encode(cityList)
}
The City type model that's used to create the cityList object holds the correct values.
type City struct {
Name string `bson:"name" json:"name"`
Country string `bson:"country" json:"country"`
}
I've been trying to return the country_name along with the city name but I can't figure how to write it into the filter properly.

How to get the value inside a primitive M

showInfoCursor, err := collection.Aggregate(context.TODO(), mongo.Pipeline{unwindStage, groupStage})
if err != nil {
panic(err)
}
var showsWithInfo []bson.M
if err = showInfoCursor.All(context.TODO(), &showsWithInfo); err != nil {
panic(err)
}
I'm iterating the showsWithInfo array. And each bson.M contains a primitive M type value for a particular key. I've tried to convert it to a struct, but it was no successful.
map[operatorId:1 channel: XYZ]
This is what I got once I print the value of that primitive M.
I need to get those two values from that. (operatorId, channel)
bson.M is a type alias to primitive.M:
type M = primitive.M
And primitive.M is a "simple" map:
type M map[string]interface{}
So you may index the value as you would any maps:
m := primitive.M{
"operatorId": 1,
"channel": "XYZ",
}
fmt.Println(m)
fmt.Println("Operator ID:", m["operatorId"])
fmt.Println("Channel:", m["channel"])
This outputs (try it on the Go Playground):
map[channel:XYZ operatorId:1]
Operator ID: 1
Channel: XYZ

How to add values to an bson.D object

I'm working with golang and the MongoDB driver, I want to patch one of my objects according to the data I get from the outside:
I have a struct:
type Pivot struct {
Email string `json:"email"`
Base string `json:"base"`
}
And the patch (with MongoDB Update)
setMap := bson.D{
{"$set", setElements},
}
res, err := collection.UpdateMany(
ctx,
filter,
setMap,
)
And I want to make the setObject a little bit dynamic:
if len(pivot.Base) > 0 {
setElements.append("base", pivot.Base) //this doesn't work...
}
if len(pivot.Email) > 0 {
setElements.append("email", pivot.Email)
}
I' ve seen that the setObject can be built like
{"$set", bson.D{
{"processed", pivot.Processed},
}
But how can I make it dynamic?
Append a DocElem (mgo) or an E (go.mongodb.org) to the slice depending on the client you are using.
var setElements bson.D
if len(pivot.Base) > 0 {
setElements = append(setElements, bson.E{"base", pivot.Base})
}
if len(pivot.Email) > 0 {
setElements = append(setElements, bson.E{"email", pivot.Email})
}
setMap := bson.D{
{"$set", setElements},
}
Replace bson.E with bson.DocElem for mgo.

How will we entered the data according to the number of result and also according the count field?

By using go api I'm retrieving the an array object. like given below:-
[
{0 1 Sunday 1 21600 25200 1}
{0 1 Sunday 2 28800 32400 2}
{0 1 Sunday 3 36000 39600 1}
]
This data will be arranged using struct:-
type ProviderSpot struct {
Id int `json:"_id" bson:"_id"`
PId int `json:"pid" bson:"pid"`
Day string `json:"day" bson:"day"`
TimeSlug int `json:"time_slug" bson:"time_slug"`
StartTime int64 `json:"start_time" bson:"start_time"`
EndTime int64 `json:"end_time" bson:"end_time"`
Count int `json:"count" bson:"count"`
}
type ProviderSpots []ProviderSpot
See in the array object I have an count values in each object 1,2,1 then I have to store this record like that those record having count they will store in the available_spot only one time means that the upper record will save in the collection only one time having there count value 1 after that the left record will remains there count value 0,1,0 then
those record having there count field value more than 0 they will save that number of times having count value in the addition_spot. The code of golang I'm using is this:-
func SaveProviderSpot(c *gin.Context) {
response := ResponseController{}
values := c.PostForm("array")
var err error
byt := []byte(values)
var result models.ProviderSpots
if err = json.Unmarshal(byt, &result); err != nil{
fmt.Println(err)
}
fmt.Println(result)
for i := 0; i < len(result); i++ {
lastValue :=result[i].Count-1
if lastValue != -1 {
providerspot.PId = result[i].PId
providerspot.Day = result[i].Day
providerspot.TimeSlug = result[i].TimeSlug
providerspot.StartTime = result[i].StartTime
providerspot.EndTime = result[i].EndTime
providerspot.Count = result[i].Count - lastValue
id, _ := models.GetAutoIncrementCounter(config.ProvidersSpotsCounterId, config.ProvidersSpotsCollection)
providerspot.Id = id
fmt.Println("Here We go now :- ", &providerspot)
err = models.AddProviderSpot(&providerspot)
}
}
}
Give some example of this which will solve this. Thanks for your valueable time spending on this question.
I solved that question answer but Can anyone tell me that will right for my code or not:-
func SaveProviderSpot(c *gin.Context) {
response := ResponseController{}
values := c.PostForm("array")
var err error
byt := []byte(values)
var result models.ProviderSpots
if err = json.Unmarshal(byt, &result); err != nil{
fmt.Println(err)
}
fmt.Println(result)
for i := 0; i < len(result); i++ {
for j := 1; j <= result[i].Count; j++ {
// lastValue := result[i].Count-1
// if lastValue != -1 {
if j == 1{
providerspot.PId = result[i].PId
providerspot.Day = result[i].Day
providerspot.TimeSlug = result[i].TimeSlug
providerspot.StartTime = result[i].StartTime
providerspot.EndTime = result[i].EndTime
providerspot.Count = 1//result[i].Count - lastValue
id, _ := models.GetAutoIncrementCounter(config.ProvidersSpotsCounterId, config.ProvidersSpotsCollection)
providerspot.Id = id
fmt.Println("Here We go now :- ", &providerspot)
err = models.AddProviderSpot(&providerspot)
}else{
providerspot.PId = result[i].PId
providerspot.Day = result[i].Day
providerspot.TimeSlug = result[i].TimeSlug
providerspot.StartTime = result[i].StartTime
providerspot.EndTime = result[i].EndTime
providerspot.Count = 1//result[i].Count - lastValue
id, _ := models.GetAutoIncrementCounter(config.AdditionalProviderCounterSpot, config.AdditionalProviderSpot)
providerspot.Id = id
err = models.AddAdditionalProviderSpot(&providerspot)
}
}
}
}
This will do want I want but I'm confused that it is right for me or not.

mgo find convert a single value array to string

This is part of my collection schema in mongodb:
{ "_id" : ObjectId("55e1eef5255da6d384754642"), "name" : [ "Web, Mobile & Software Dev", "Movil y desarrollo de software" ] } { "_id" : ObjectId("55e1f2d0255da6d38475464b"), "name" : [ "IT & Networking", "TI y Redes" ] } ...
Right now i can get the info like this:
err := r.Coll.Find(bson.M{}).Select(bson.M{"name": bson.M{"$slice": []int{1, 1}}}).All(&result.Data)
but i want "name" to return a string instead of a single value array, so i dont have to index it inside my frontend if no need.
very limited comments i need 2000 poits for editing my post and add more things it seems, this is not answer but maybe, so i have to loop it?, isnt better way?
err := r.Coll.Find(bson.M{}).Select(bson.M{"name": bson.M{"$slice": []int{1, 1}}}).All(&result.Data)
if err != nil {
return result, err
}
type skillnew struct {
Id bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"`
Name string `bson:"name,omitempty" json:"name,omitempty"`
}
skillsallnew := make([]skillnew, len(result.Data))
for i := range result.Data {
skillsallnew[i] = skillnew{result.Data[i].Id, result.Data[i].Name[0]}
}