package zoom // Query represents a query which will retrieve some models from // the database. A Query may consist of one or more query modifiers // (e.g. Filter or Order) and may be executed with a query finisher // (e.g. Run or IDs). type Query struct { *query } // NewQuery is used to construct a query. The query returned can be chained // together with one or more query modifiers (e.g. Filter or Order), and then // executed using the Run, RunOne, Count, or IDs methods. If no query modifiers // are used, running the query will return all models of the given type in // unspecified order. Queries use delayed execution, so nothing touches the // database until you execute them. func (collection *Collection) NewQuery() *Query { return &Query{ query: newQuery(collection), } } // Order specifies a field by which to sort the models. fieldName should be a // field in the struct type corresponding to the Collection used in the query // constructor. By default, the records are sorted by ascending order by the // given field. To sort by descending order, put a negative sign before the // field name. Zoom can only sort by fields which have been indexed, i.e. those // which have the `zoom:"index"` struct tag. Only one order may be specified per // Order will set an error on the query if the fieldName is invalid, if another // order has already been applied to the query, or if the fieldName specified // does not correspond to an indexed field. The error, same as any other error // that occurs during the lifetime of the query, is not returned until the query // is executed. func (q *Query) Order(fieldName string) *Query { q.query.Order(fieldName) return q } // Limit specifies an upper limit on the number of models to return. If amount // is 0, no limit will be applied and any number of models may be returned. The // default value is 0. func (q *Query) Limit(amount uint) *Query { q.query.Limit(amount) return q } // Offset specifies a starting index (inclusive) from which to start counting // models that will be returned. For example, if offset is 10, the first 10 // models that the query would otherwise return will be skipped. The default // value is 0. func (q *Query) Offset(amount uint) *Query { q.query.Offset(amount) return q } // Include specifies one or more field names which will be read from the // database and scanned into the resulting models when the query is run. Field // names which are not specified in Include will not be read or scanned. You can // only use one of Include or Exclude, not both on the same query. Include will // set an error if you try to use it with Exclude on the same query. The error, // same as any other error that occurs during the lifetime of the query, is not // returned until the query is executed. func (q *Query) Include(fields ...string) *Query { q.query.Include(fields...) return q } // Exclude specifies one or more field names which will *not* be read from the // database and scanned. Any other fields *will* be read and scanned into the // resulting models when the query is run. You can only use one of Include or // Exclude, not both on the same query. Exclude will set an error if you try to // use it with Include on the same query. The error, same as any other error // that occurs during the lifetime of the query, is not returned until the query // is executed. func (q *Query) Exclude(fields ...string) *Query { q.query.Exclude(fields...) return q } // Filter applies a filter to the query, which will cause the query to only // return models with field values matching the expression. filterString should // be an expression which includes a fieldName, a space, and an operator in that // order. For example: Filter("Age >=", 30) would only return models which have // an Age value greater than or equal to 30. Operators must be one of "=", "!=", // ">", "<", ">=", or "<=". You can only use Filter on fields which are indexed, // i.e. those which have the `zoom:"index"` struct tag. If multiple filters are // applied to the same query, the query will only return models which have // matches for *all* of the filters. Filter will set an error on the query if // the arguments are improperly formated, if the field you are attempting to // filter is not indexed, or if the type of value does not match the type of the // field. The error, same as any other error that occurs during the lifetime of // the query, is not returned until the query is executed. func (q *Query) Filter(filterString string, value interface{}) *Query { q.query.Filter(filterString, value) return q } // Run executes the query and scans the results into models. The type of models // should be a pointer to a slice of Models. If no models fit the criteria, Run // will set the length of models to 0 but will *not* return an error. Run will // return the first error that occurred during the lifetime of the query (if // any), or if models is the wrong type. func (q *Query) Run(models interface{}) error { tx := q.pool.NewTransaction() newTransactionQuery(q.query, tx).Run(models) return tx.Exec() } // RunOne is exactly like Run but finds only the first model that fits the query // criteria and scans the values into model. If no model fits the criteria, // RunOne *will* return a ModelNotFoundError. func (q *Query) RunOne(model Model) error { tx := q.pool.NewTransaction() newTransactionQuery(q.query, tx).RunOne(model) return tx.Exec() } // Count counts the number of models that would be returned by the query without // actually retrieving the models themselves. Count will also return the first // error that occurred during the lifetime of the query (if any). func (q *Query) Count() (int, error) { tx := q.pool.NewTransaction() var count int newTransactionQuery(q.query, tx).Count(&count) if err := tx.Exec(); err != nil { return 0, err } return count, nil } // IDs returns only the ids of the models without actually retrieving the // models themselves. IDs will return the first error that occurred during the // lifetime of the query (if any). func (q *Query) IDs() ([]string, error) { tx := q.pool.NewTransaction() ids := []string{} newTransactionQuery(q.query, tx).IDs(&ids) if err := tx.Exec(); err != nil { return nil, err } return ids, nil } // StoreIDs executes the query and stores the model ids matching the query // criteria in a list identified by destKey. The list will be completely // overwritten, and the model ids stored there will be in the correct order if // the query includes an Order modifier. StoreIDs will return the first error // that occurred during the lifetime of the query (if any). func (q *Query) StoreIDs(destKey string) error { tx := q.pool.NewTransaction() newTransactionQuery(q.query, tx).StoreIDs(destKey) return tx.Exec() }