Search code examples
gomgo

Find all elements sorted by best matches


I have some entities in my database called "events". Each of these events contain an array of string, called "tags".

I want to make a query to get all the events matching an array of tags that I will give in parameter.

BUT, I want these events to be sorted like:

  • The first one is the one containing most of the tags I give in parameter.
  • The second one is the second one containing most of the tags I give in parameter.
  • And so on.

If there are more than one event containing the same amount of tags I want them to be sorted by their "name" property in alphabetical order.

Example:

  • "name" = "event1", "tags" = ["food", "music", "gaming", "sport"]
  • "name" = "event2", "tags" = ["gaming"]
  • "name" = "event3", "tags" = ["music", "sport"]
  • "name" = "event4", "tags" = ["food", "music", "gaming", "sport"]
  • "name" = "event5", "tags" = ["music", "sport", "coding"]
  • "name" = "event6", "tags" = ["coding"]
  • "name" = "event7", "tags" = ["food", "gaming", "sport"]

The array of tags that I give in parameter for this example is: ["food", "music", "gaming", "sport"]

The result for this example will be an array of events, containing in that order: event1, event4, event7, event3, event5, event2

To get all the events containing the tags I simply make a query with the "$or" operators. This allowed me to get all the events if they contained at least one of the tag given in parameter.

Code:

    var events []model.Event

    var MyQuery []map[string]interface{}
    for i := 0; i < len(tags); i++ {
        currentCondition := bson.M{"tags": tags[i]}
        MyQuery = append(MyQuery, currentCondition)
    }

    err := dbEvents.C(collectionEvents).Find(bson.M{"$or": OrQuery}).All(&events)

But i really don't know how to sort them like I showed you.


Solution

  • package main
    
    import (
        "fmt"
    
        "github.com/ahmetb/go-linq"
    )
    
    type T struct {
        Name string
        Tags []string
    }
    
    func main() {
        params := []string{"food", "music", "gaming", "sport"}
        t := []T{
            T{Name: "event1", Tags: []string{"food", "music", "gaming", "sport"}},
            T{Name: "event2", Tags: []string{"gaming"}},
            T{Name: "event3", Tags: []string{"music", "sport"}},
            T{Name: "event4", Tags: []string{"food", "music", "gaming", "sport"}},
            T{Name: "event5", Tags: []string{"music", "coding", "sport"}},
            T{Name: "event6", Tags: []string{"coding"}},
            T{Name: "event7", Tags: []string{"food", "gaming", "sport"}},
        }
        var result []T
        linq.From(t).SortT(func(t1 T, t2 T) bool {
            var rs1 []string
            linq.From(t1.Tags).IntersectByT(linq.From(params), func(str string) string {
                return str
            }).ToSlice(&rs1)
            var rs2 []string
            linq.From(t2.Tags).IntersectByT(linq.From(params), func(str string) string {
                return str
            }).ToSlice(&rs2)
            return len(rs1) > len(rs2)
        }).ToSlice(&result)
    
        fmt.Printf("%+v", result)
    }
    

    [{Name:event1 Tags:[food music gaming sport]} {Name:event4 Tags:[food music gaming sport]} {Name:event7 Tags:[food gaming sport]} {Name:event3 Tags:[music sport]} {Name:event5 Tags:[music coding sport]} {Name:event2 Tags:[gaming]} {Name:event6 Tags:[coding]}]

    Above program sorts the array as per your requirements, Hope this will help you.