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
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.
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
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.
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.
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]}
}