99designs / gqlgen

go generate based graphql server library
https://gqlgen.com
MIT License
9.94k stars 1.16k forks source link

[][]Model, doubts about sorted returned array, using dataloader #683

Closed frederikhors closed 5 years ago

frederikhors commented 5 years ago

Question for newbies like me. Eventually, if that's okay with you, I can open a wiki page to collect all these FAQs for newbies.

I'm experimenting with gqlgen and dataloader pattern using dataloaden.

These are the models:

type School struct {
  ID   int
  Desc string
}

type Author struct {
  ID       int
  Name     string
  SchoolID int
}

type Book struct {
  ID       int
  Pages    int
  AuthorID int
}

This is the schema.graphql:

type School {
  id: ID
  desc: String
  authors: [Author]
}

type Author {
  id: ID
  name: String
  books: [Book]
}

type Book {
  id: ID
  pages: Int
  author: Author
}

I'm also using go-pg ORM (sqlboiler is planned) like this:

loaders.booksByAuthor = &BooksSliceLoader{
  wait:     1 * time.Millisecond,
  maxBatch: 100,
  fetch: func(keys []int) ([][]Book, []error) {
    var dbItems []Book
    keysIn := pg.In(keys)
    if err := DB.Model(&dbItems).Where("author_id in (?)", keysIn).OrderExpr("array_position(ARRAY[?]::int[], author_id::int)", keysIn).Select(); err != nil {
      return nil, []error{err}
    }
    items := make([][]Book, len(keys))
    for i, key := range keys {
      for _, dbItem := range dbItems {
        if dbItem.AuthorID == key {
          items[i] = append(items[i], dbItem)
        }
      }
    }
    return items, nil
  },
}

QUESTION

I was wondering if the below is an optimized way to re-order the [][]Book array coming from DB every time.

I'm asking about this code:

items := make([][]Book, len(keys))
for i, key := range keys {
  for _, dbItem := range dbItems {
    if dbItem.AuthorID == key {
      items[i] = append(items[i], dbItem)
    }
  }
}

Am I totally wrong?

Is this ok with the Golang Garbage Collector?

Daavidaviid commented 5 years ago

Hey,

I too am using gqlgen in a project with go-pg. Personally, I do something like that :

ldrs.EmployeesByStore = &EmployeeSliceLoader{
            wait:     wait,
            maxBatch: 100,
            fetch: func(storeIDs []string) ([][]employee.Employee, []error) {
                var employees []employee.Employee

                employeesByStoreID := make([][]employee.Employee, len(storeIDs))
                errors := make([]error, len(storeIDs))

                err := database.DB.Model(&employee.Employee{}).
                    Where("store_id in (?)", pg.In(storeIDs)).
                    Select(&employees)

                for index, storeID := range storeIDs {
                    for _, employee := range employees {
                        if employee.StoreID == storeID {
                            employeesByStoreID[index] = append(employeesByStoreID[index], employee)
                        }
                    }
                }

                if err != nil {
                    errors := []error{err}
                    return [][]employee.Employee{}, errors
                }

                return employeesByStoreID, errors
            },
        }
vektah commented 5 years ago

https://github.com/99designs/gqlgen/pull/758 makes this a bit clearer