I need to create some variation for the user, so that he can select only those users for whom he specifies a category (search by category) or those who do not have the same categories as in the array (in the code you can see the array).
I used the documentation and found this answer: operator $ne
But it is not works, I get a list of all users
func (r *Mongo) User(ctx context.Context, query *domain.Query) ([]*User, error) {
var filter interface{}
if query.Query != "" {
filter = bson.D{primitive.E{Key: "status", Value: true}, primitive.E{Key: "category", Value: query.Query}}
} else {
filter = bson.D{primitive.E{Key: "status", Value: true}}
} - this works
if query.OtherCategory {
category := []string{"it", "medical", "sport"}
filter = bson.M{"status": true, "category": bson.M{"$ne": category}}
} - this it is not works
cursor, err := r.col.Find(ctx, filter, opts)
var results []*domain.User
if err = cursor.All(ctx, &results); err != nil {
r.logger.Error().Err(err).Msg("failed find")
return nil, err
}
return results, nil
}
How to make a query to get a list of users who don't have these (array of categories in the code) categories in the array, and their status is true?
$ne ("not equal") will list you documents where the category field of the user is not the given array.
But this is not you want: you want to list users where the user has none of the given categories, or in other words the intersection of the user's categories and the given categories is empty.
For this you have to use the $nin ("not in") operator: list users where the category is not in the given array. For array fields this will be checked for all array elements.
if query.OtherCategory {
category := []string{"it", "medical", "sport"}
filter = bson.M{"status": true, "category": bson.M{"$nin": category}}
}
Related
I want to print out the first "row" of my JSON that is within a struct in Go. The JSON looks like
[
{
"id":"7",
"username":"user",
"subject":"subject",
"message":"message"
},
{
"id":"6",
"username":"user2",
"subject":"subject2",
"message":"message2"
},
{
"id":"5",
"username":"user3",
"subject":"subject3",
"message":"message3"
},
{
"id":"4",
"username":"user4",
"subject":"subject4",
"message":"message4"
},
{
"id":"3",
"username":"user5",
"subject":"subject5",
"message":"message5"
},
{
"id":"2",
"username":"user6",
"subject":"subject6",
"message":"message6"
},
{
"id":"1",
"username":"user7",
"subject":"subject7",
"message":"message7"
}
]
I have put it in a Struct like this
type Info struct {
Id string
Username string
Subject string
Message string
}
infoJson := html;
var information []Info;
err2 := json.Unmarshal([]byte(infoJson), &information);
if err2 != nil {
fmt.Println(err2);
}
And then I can print all of them out using
for _, info := range information {
fmt.Println(info.Id + " " + info.Username);
fmt.Println(info.Subject);
fmt.Println(info.Message);
}
I would like to just be able to print out the JSON that is aligned with a specific id. For example, I wish to be able to specify 7 and then all the things that are in the id:7 JSON row will be printed out in the above format. So it should print out:
7 user
subject
message
How can I do this?
If you want to print the "first" item. Then you can certainly use the index of the item to do that.
fmt.Println(information[0])
If you want to print a specific item, then you would have to iterate using range and check if the item matches the condition.
It may be more helpful to build a map of the items, in this case using ID as the key.
// Print the first item.
fmt.Println(information[0])
// Create a map to index items by ID.
dictionary := make(map[string]Info)
for _, info := range information {
dictionary[info.Id] = info
}
element, ok := dictionary["7"]
if !ok {
fmt.Println("Not found.")
return
}
fmt.Println(element)
You can also add a method on Info to contain the formatting logic.
func (i *Info) Print() string {
return fmt.Sprintf("%s %s\n%s\n%s\n", i.Id, i.Username, i.Subject, i.Message)
}
And then simply call that:
// Print the first item.
fmt.Println(information[0].Print())
I have this data schema:
"person": {
"name": "John",
"pets": [
{
"name": "Birdie"
}
]
}
This is the struct to insert the Person document to MongoDB:
type Person struct {
Id primitive.ObjectID `bson:"_id,omitempty" json:"id"`
Name string `json:"name"`
Pets []struct {
Name string `json:"name"`
} `json:"pets"
}
When JSON is sent to the POST Person API without the pets field, the MongoDB document recorded has pets field set as null. I think this is because slices in go has zero value of nil instead of empty array?
personPostRequest := ds.Person{}
if err := c.ShouldBindJSON(&personPostRequest); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
Having pets initialized as null is troublesome because I can't use addToSet when adding pets to the document:
// Will render the error Cannot apply $addToSet to non-array field.
// Field named 'pets' has non-array type null
err = collection.FindOneAndUpdate(
ctx,
bson.M{"_id": personId},
bson.M{
"$addToSet": bson.M{
"pets": bson.M{"$each": pets},
},
},
&opt,
)
I can solve this problem by adding the struct tag bson:,omitempty to pets but I like to explore the solution where I can initialized pets to empty array in MongoDB.
How do I do this in Go? I'm using Go gin framework. Thanks
I have two arrays of users and beams
struct User {
var id: Int
var jobTitle: String
}
struct Beams {
var isSelected = false
var id: Int
var userIds: [Int]
}
If the user selects a particular beam, all the users which are assigned to that beam will be selected of the first array. The user can select multiple beams and i am getting the data from api response, and i have to send the selected users in a post call. so i have to select all the selected users from an array of beams and on the basis of filtered ids, send that array in a post call. initially i am filtering all the beams which are selected like this
beams.filter { $0.isSelected }.compactMap { $0.userIDS }
which gives me an array of [Int]. These are the userIds which have to be sent. I somehow can't figure out how will i select these particular ids from my array of users which contains other attributes as well. contains can be used to filter one element but not a sequence. and even if i filter or take an intersection of these ids and my userArray. i am still left with a set or an array of ids. from which i'd have to generate my array, i want to keep all the other attributes as well. Any help in the right direction would be appreciated. I have something of this nature in my mind
let selectedBeamsIds = beams.filter { $0.isSelected }.compactMap { $0.userIDS }
users.append(selectedBeamsIds.compactMap { getUniqueUsersForBeams(ids: $0) } )
private func getUniqueUsersForBeams(ids: [Int]) -> [User] {
let ower = users.reduce(into: [User]()) { (result,
user) in
let filteredUserIds = ids.filter { $0 == user.id }
//result.append(use)
}
}
I also need to do this with another array of jobTitles which is like
struct JobTitle {
var jobTitle: String
var isSelected = false
}
this one is straight forward as if the users's jobTitle matches with the selected items of [String] array then select that user to be sent.
Try,
let selectedIds = beams.filter { (beams) -> Bool in
beams.isSelected
}.flatMap { (beams) -> [Int] in
beams.userIds
}
let filteredUsers = users.filter { (user) -> Bool in
selectedIds.contains(user.id)
}
1 ) Filter out selected beams
2 ) Collect all the userIds in the selected beams
3 ) Filter users by checking if the id is present in the previously collected ids.
struct Test {
var title: String
var message: [String?: String?]
init(title: String, message: [String?:String?]) {
self.title = title
self.message = message
}
}
var cases = [
Test(title: "1", message: ["tag1": nil]),
Test(title: "2", message: ["tag2": "preview2"]),
Test(title: "3", message: [nil:nil]),
Test(title: "4", message: ["tag1":"preview4"])
]
Now, I want:
An array with all keys from message property from cases - tag1 and tag2 (no nils in it). I just tried everything I know, I couldn't do it. Tried with filtering cases, got optionals.
There are no previews without a tag, so there is no need for an array with them. I only need a list with the tags, in order to sort it and show the relevant previews from the cases. That's why I need to know a way how to access these previews from the cases. Let's say in a UITableView:
cell.previewLabel?.text = cases[indexPath.row].preview[//No idea what here]
Of course, a dictionary with [tags: previews] would also be perfect!
Thanks in advance! I hope what I want is possible.
Here is an array that only contains elements from cases that have all keys and values not nil :
let filtered = cases.filter { test in
return test.message.allSatisfy({ entry in
return entry.key != nil && entry.value != nil
})
}
Or using the shorthand notation :
let filtered = cases.filter {
$0.message.allSatisfy({
$0.key != nil && $0.value != nil
})
}
With structs there is a default initializer, so you can write your Test struct this way:
struct Test {
var title: String
var message: [String?: String?]
}
It's not completely clear to me what you're attempting to do, however, this will filter your cases array to only Test objects that contain non-nil values in the message dictionary:
let nonNil = cases.filter { (test) -> Bool in
return Array(test.message.values).filter({ (value) -> Bool in
return value == nil
}).count <= 0
}
The variable nonNil now contains the Test objects where title is "2" and title is "4".
You could further filter that if you want a [tags:preview] dictionary. Something like this would do that:
let tags = nonNil.map( { $0.message} ).flatMap { $0 }.reduce([String:String]()) { (accumulator, current) -> [String:String] in
guard let key = current.key, let value = current.value else { return accumulator }
var accum = accumulator
accum.updateValue(value, forKey: key)
return accum
}
The tags dictionary now contains: ["tag1": "preview4", "tag2": "preview2"]
I have an array of Categories. Each Category instance has offers property.
class Category {
var offers : [Offer]?
var title : String?
var id : Int?
}
class Offer {
var type : String?
}
//global variable
var categories = [ categ1, categ2, ...]
How can I filter categories by offer.type ?
I already have tried:
return categories.map { (category) -> Category in
let offers = category.offers?.filter { $0.type == myType }
category.offers = offers
return category
}
It works but after calling function second time array becomes empty. Probably because offers were rewritten?
Then I have tried this (produced same wrong result):
var resultCategories = [Category]()
for category in categories {
guard let offers = category.offers else { continue }
var newOffers = [Offer]()
for offer in offers {
if offer.type == myType {
newOffers.append(offer)
}
}
category.offers = newOffers
resultCategories.append(category)
}
return resultCategories
You should simply filter all categories that have no offers equals to your type. You can achieve that by:
filter all your categories and
inside the filter check if current offers contains the myType
Code:
let filtered = categories.filter { category in
category.offers?.contains(where: { $0.type == myType }) ?? false
}
And note, that category.offers?.[...] is optional value, so the ?? false returns false as result if left part is nil.
UPD.
But I expected that categories will have only offers with type = "A". Maybe I did not described the question accurately.
You can achieve that by creating a new Category.
let filtered = categories.compactMap { category -> Category? in
guard let offers = category.offers?.filter({ $0.type == "A" }) else { return nil }
let other = Category()
other.offers = offers
return other
}
Also note, i'm using compactMap. It allows me to filter categories with empty or nil offers out.
You can use simple and easy filter(functional programming) instead of for-loop.
First filter category then check offers contains particular type or not(using equal to condition)
let data = categories.filter { ($0.offers?.contains(where: {$0.type == "yourtype"})) ?? false
}
If you want multiple filter, like one field from one model and second field from nested array, please che
let searchText = "a"
let filteredCategoryList = list.filter { category in
let categoryFilter = category.title?.range(of: searchText, options: .caseInsensitive) != nil
let offerFilter = category.offers?.contains(where: { $0.type?.range(of: searchText, options: .caseInsensitive) != nil
})
return categoryFilter || offerFilter ?? false
}