github.com/rjgonzale/pop/v5@v5.1.3-dev/query.go (about)

     1  package pop
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/gobuffalo/pop/v5/logging"
     8  )
     9  
    10  // Query is the main value that is used to build up a query
    11  // to be executed against the `Connection`.
    12  type Query struct {
    13  	RawSQL                  *clause
    14  	limitResults            int
    15  	addColumns              []string
    16  	eagerMode               EagerMode
    17  	eager                   bool
    18  	eagerFields             []string
    19  	whereClauses            clauses
    20  	orderClauses            clauses
    21  	fromClauses             fromClauses
    22  	belongsToThroughClauses belongsToThroughClauses
    23  	joinClauses             joinClauses
    24  	groupClauses            groupClauses
    25  	havingClauses           havingClauses
    26  	Paginator               *Paginator
    27  	Connection              *Connection
    28  }
    29  
    30  // Clone will fill targetQ query with the connection used in q, if
    31  // targetQ is not empty, Clone will override all the fields.
    32  func (q *Query) Clone(targetQ *Query) {
    33  	rawSQL := *q.RawSQL
    34  	targetQ.RawSQL = &rawSQL
    35  
    36  	targetQ.limitResults = q.limitResults
    37  	targetQ.whereClauses = q.whereClauses
    38  	targetQ.orderClauses = q.orderClauses
    39  	targetQ.fromClauses = q.fromClauses
    40  	targetQ.belongsToThroughClauses = q.belongsToThroughClauses
    41  	targetQ.joinClauses = q.joinClauses
    42  	targetQ.groupClauses = q.groupClauses
    43  	targetQ.havingClauses = q.havingClauses
    44  	targetQ.addColumns = q.addColumns
    45  
    46  	if q.Paginator != nil {
    47  		paginator := *q.Paginator
    48  		targetQ.Paginator = &paginator
    49  	}
    50  
    51  	if q.Connection != nil {
    52  		connection := *q.Connection
    53  		targetQ.Connection = &connection
    54  	}
    55  }
    56  
    57  // RawQuery will override the query building feature of Pop and will use
    58  // whatever query you want to execute against the `Connection`. You can continue
    59  // to use the `?` argument syntax.
    60  //
    61  //	c.RawQuery("select * from foo where id = ?", 1)
    62  func (c *Connection) RawQuery(stmt string, args ...interface{}) *Query {
    63  	return Q(c).RawQuery(stmt, args...)
    64  }
    65  
    66  // RawQuery will override the query building feature of Pop and will use
    67  // whatever query you want to execute against the `Connection`. You can continue
    68  // to use the `?` argument syntax.
    69  //
    70  //	q.RawQuery("select * from foo where id = ?", 1)
    71  func (q *Query) RawQuery(stmt string, args ...interface{}) *Query {
    72  	q.RawSQL = &clause{stmt, args}
    73  	return q
    74  }
    75  
    76  // Eager will enable associations loading of the model.
    77  // by defaults loads all the associations on the model,
    78  // but can take a variadic list of associations to load.
    79  //
    80  // 	c.Eager().Find(model, 1) // will load all associations for model.
    81  // 	c.Eager("Books").Find(model, 1) // will load only Book association for model.
    82  //
    83  // Eager also enable nested models creation:
    84  //
    85  //	model := Parent{Child: Child{}, Parent: &Parent{}}
    86  //	c.Eager().Create(&model) // will create all associations for model.
    87  //	c.Eager("Child").Create(&model) // will only create the Child association for model.
    88  func (c *Connection) Eager(fields ...string) *Connection {
    89  	con := c.copy()
    90  	con.eager = true
    91  	con.eagerFields = append(c.eagerFields, fields...)
    92  	return con
    93  }
    94  
    95  // Eager will enable load associations of the model.
    96  // by defaults loads all the associations on the model,
    97  // but can take a variadic list of associations to load.
    98  //
    99  // 	q.Eager().Find(model, 1) // will load all associations for model.
   100  // 	q.Eager("Books").Find(model, 1) // will load only Book association for model.
   101  func (q *Query) Eager(fields ...string) *Query {
   102  	q.eager = true
   103  	q.eagerFields = append(q.eagerFields, fields...)
   104  	return q
   105  }
   106  
   107  // disableEager disables eager mode for current query and Connection.
   108  func (q *Query) disableEager() {
   109  	q.Connection.eager, q.eager = false, false
   110  	q.Connection.eagerFields, q.eagerFields = []string{}, []string{}
   111  }
   112  
   113  // Where will append a where clause to the query. You may use `?` in place of
   114  // arguments.
   115  //
   116  // 	c.Where("id = ?", 1)
   117  // 	q.Where("id in (?)", 1, 2, 3)
   118  func (c *Connection) Where(stmt string, args ...interface{}) *Query {
   119  	q := Q(c)
   120  	return q.Where(stmt, args...)
   121  }
   122  
   123  // Where will append a where clause to the query. You may use `?` in place of
   124  // arguments.
   125  //
   126  // 	q.Where("id = ?", 1)
   127  // 	q.Where("id in (?)", 1, 2, 3)
   128  func (q *Query) Where(stmt string, args ...interface{}) *Query {
   129  	if q.RawSQL.Fragment != "" {
   130  		log(logging.Warn, "Query is setup to use raw SQL")
   131  		return q
   132  	}
   133  	if inRegex.MatchString(stmt) {
   134  		var inq []string
   135  		for i := 0; i < len(args); i++ {
   136  			inq = append(inq, "?")
   137  		}
   138  		qs := fmt.Sprintf("(%s)", strings.Join(inq, ","))
   139  		stmt = strings.Replace(stmt, "(?)", qs, 1)
   140  	}
   141  	q.whereClauses = append(q.whereClauses, clause{stmt, args})
   142  	return q
   143  }
   144  
   145  // Order will append an order clause to the query.
   146  //
   147  // 	c.Order("name desc")
   148  func (c *Connection) Order(stmt string) *Query {
   149  	return Q(c).Order(stmt)
   150  }
   151  
   152  // Order will append an order clause to the query.
   153  //
   154  // 	q.Order("name desc")
   155  func (q *Query) Order(stmt string) *Query {
   156  	if q.RawSQL.Fragment != "" {
   157  		log(logging.Warn, "Query is setup to use raw SQL")
   158  		return q
   159  	}
   160  	q.orderClauses = append(q.orderClauses, clause{stmt, []interface{}{}})
   161  	return q
   162  }
   163  
   164  // Limit will create a query and add a limit clause to it.
   165  //
   166  // 	c.Limit(10)
   167  func (c *Connection) Limit(limit int) *Query {
   168  	return Q(c).Limit(limit)
   169  }
   170  
   171  // Limit will add a limit clause to the query.
   172  //
   173  // 	q.Limit(10)
   174  func (q *Query) Limit(limit int) *Query {
   175  	q.limitResults = limit
   176  	return q
   177  }
   178  
   179  // Preload activates preload eager Mode automatically.
   180  func (c *Connection) EagerPreload(fields ...string) *Query {
   181  	return Q(c).EagerPreload(fields...)
   182  }
   183  
   184  // Preload activates preload eager Mode automatically.
   185  func (q *Query) EagerPreload(fields ...string) *Query {
   186  	q.Eager(fields...)
   187  	q.eagerMode = EagerPreload
   188  	return q
   189  }
   190  
   191  // Q will create a new "empty" query from the current connection.
   192  func Q(c *Connection) *Query {
   193  	return &Query{
   194  		RawSQL:      &clause{},
   195  		Connection:  c,
   196  		eager:       c.eager,
   197  		eagerFields: c.eagerFields,
   198  		eagerMode:   eagerModeNil,
   199  	}
   200  }
   201  
   202  // ToSQL will generate SQL and the appropriate arguments for that SQL
   203  // from the `Model` passed in.
   204  func (q Query) ToSQL(model *Model, addColumns ...string) (string, []interface{}) {
   205  	sb := q.toSQLBuilder(model, addColumns...)
   206  	return sb.String(), sb.Args()
   207  }
   208  
   209  // ToSQLBuilder returns a new `SQLBuilder` that can be used to generate SQL,
   210  // get arguments, and more.
   211  func (q Query) toSQLBuilder(model *Model, addColumns ...string) *sqlBuilder {
   212  	if len(q.addColumns) != 0 {
   213  		addColumns = q.addColumns
   214  	}
   215  	return newSQLBuilder(q, model, addColumns...)
   216  }