3
0

Filtering records by exact value of a single field

This commit is contained in:
Mitja Zivkovic
2018-12-26 10:05:14 +01:00
parent 5022abd466
commit 00f04dd498
8 changed files with 90 additions and 55 deletions

View File

@@ -676,10 +676,10 @@
],
"get": [
{
"name": "filter",
"type": "string",
"name": "query",
"required": false,
"title": "Search query"
"title": "Filtering condition"
},
{
"name": "page",

View File

@@ -160,9 +160,9 @@
"Parameters": {
"get": [
{
"name": "query",
"name": "filter",
"required": false,
"title": "Search query",
"title": "Filtering condition",
"type": "string"
},
{

View File

@@ -3,12 +3,13 @@ package repository
import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/crusttech/crust/crm/repository/ql"
"github.com/pkg/errors"
"github.com/titpetric/factory"
"gopkg.in/Masterminds/squirrel.v1"
"github.com/crusttech/crust/crm/types"
)
@@ -20,7 +21,7 @@ type (
FindByID(id uint64) (*types.Record, error)
Report(moduleID uint64, metrics, dimensions, filter string) (results interface{}, err error)
Find(moduleID uint64, query string, page int, perPage int, sort string) (*FindResponse, error)
Find(moduleID uint64, filter string, sort string, page int, perPage int) (*FindResponse, error)
Create(mod *types.Record) (*types.Record, error)
Update(mod *types.Record) (*types.Record, error)
@@ -30,7 +31,7 @@ type (
}
FindResponseMeta struct {
Query string `json:"query,omitempty"`
Filter string `json:"filter,omitempty"`
Page int `json:"page"`
PerPage int `json:"perPage"`
Count int `json:"count"`
@@ -101,7 +102,7 @@ func (r *record) Report(moduleID uint64, metrics, dimensions, filter string) (re
}
}
func (r *record) Find(moduleID uint64, query string, page int, perPage int, sort string) (*FindResponse, error) {
func (r *record) Find(moduleID uint64, filter string, sort string, page int, perPage int) (*FindResponse, error) {
if page < 0 {
page = 0
}
@@ -116,22 +117,58 @@ func (r *record) Find(moduleID uint64, query string, page int, perPage int, sort
}
response := &FindResponse{
Meta: FindResponseMeta{
Filter: filter,
Page: page,
PerPage: perPage,
Query: query,
Sort: sort,
},
Records: make([]*types.Record, 0),
}
query = "%" + query + "%"
// Create query for fetching and counting records.
query := squirrel.
Select().
From("crm_record").
Where("module_id = ?", moduleID).
Where(squirrel.Eq{"deleted_at": nil})
sqlSelect := "SELECT * FROM crm_record"
sqlCount := "SELECT count(*) FROM crm_record"
sqlWhere := "WHERE module_id=? and deleted_at IS NULL"
sqlOrder := "ORDER BY id DESC"
sqlLimit := fmt.Sprintf("LIMIT %d, %d", page*perPage, perPage)
// Parse filters.
p := ql.NewParser()
p.OnIdent = ql.MakeIdentWrapHandler(jsonWrap, "created_at", "updated_at", "id", "user_id")
where, err := p.ParseExpression(filter)
if err != nil {
return nil, err
}
// Append filtering to query.
query = query.Where(where)
// Create count SQL sentences.
count := query.Column(squirrel.Alias(squirrel.Expr("COUNT(*)"), "count"))
sqlSelect, argsSelect, err := count.ToSql()
if err != nil {
return nil, err
}
// Execute count query.
if err := r.db().Get(&response.Meta.Count, sqlSelect, argsSelect...); err != nil {
return nil, err
}
// Return empty response if count of records is zero.
if response.Meta.Count == 0 {
return response, nil
}
// Create query for fetching records.
query = query.
Column("*").
Limit(uint64(perPage)).
Offset(uint64(page))
// Append Sorting.
chuncks := strings.Split(sort, ",")
if len(chuncks) > 0 {
@@ -195,37 +232,35 @@ func (r *record) Find(moduleID uint64, query string, page int, perPage int, sort
orderFields = append(orderFields, field+" "+order)
}
sqlOrder = "ORDER BY " + strings.Join(orderFields, ", ")
query = query.OrderBy(orderFields...)
}
// One possibility to order by field value without JSON, is query written bellow with FIELD over column names and order by value:
// SELECT * FROM crm_record
// LEFT JOIN crm_record ON crm_record.id = crm_record_column.record_id"
// WHERE column_name in ('name', 'email')
// ORDER BY FIELD(column_name, 'email', 'name'), column_value;
/*
p = ql.NewParser()
p.OnIdent = ql.MakeIdentWrapHandler(jsonWrap, "id", "module_id", "user_id", "created_at", "updated_at")
// Possibility to order with JSON:
// SELECT *,
// JSON_UNQUOTE(JSON_EXTRACT(json, REPLACE(JSON_UNQUOTE(JSON_SEARCH(json, 'all', 'email')), '.name', '.value'))) as emailField
// FROM crm_record
// ORDER by emailField asc;
order, err := p.ParseExpression(sort)
if err != nil {
return nil, err
}
switch true {
case query != "":
sqlWhere = sqlWhere + " AND id in (select distinct record_id from crm_record_column where column_value like ?)"
if err := r.db().Get(&response.Meta.Count, sqlCount+" "+sqlWhere, moduleID, query); err != nil {
return nil, err
}
if err := r.db().Select(&response.Records, sqlSelect+" "+sqlWhere+" "+sqlOrder+" "+sqlLimit, moduleID, query); err != nil {
return nil, err
}
default:
if err := r.db().Get(&response.Meta.Count, sqlCount+" "+sqlWhere, moduleID); err != nil {
return nil, err
}
if err := r.db().Select(&response.Records, sqlSelect+" "+sqlWhere+" "+sqlOrder+" "+sqlLimit, moduleID); err != nil {
sqlOrder, argsOrder, err := order.ToSql()
if err != nil {
return nil, err
}
query = query.OrderBy(sqlOrder)
*/
// Create actual fetch SQL sentences.
sqlSelect, argsSelect, err = query.ToSql()
if err != nil {
return nil, err
}
// Append order args to select args and execute actual query.
// argsSelect = append(argsSelect, argsOrder...)
if err := r.db().Select(&response.Records, sqlSelect, argsSelect...); err != nil {
return nil, err
}
return response, nil

View File

@@ -58,7 +58,7 @@ func (s *Module) RecordReport(ctx context.Context, r *request.ModuleRecordReport
}
func (s *Module) RecordList(ctx context.Context, r *request.ModuleRecordList) (interface{}, error) {
return s.content.With(ctx).Find(r.ModuleID, r.Query, r.Page, r.PerPage, r.Sort)
return s.content.With(ctx).Find(r.ModuleID, r.Filter, r.Page, r.PerPage, r.Sort)
}
func (s *Module) RecordRead(ctx context.Context, r *request.ModuleRecordRead) (interface{}, error) {

View File

@@ -320,7 +320,7 @@ var _ RequestFiller = NewModuleRecordReport()
// Module record/list request parameters
type ModuleRecordList struct {
Query string
Filter string
Page int
PerPage int
Sort string
@@ -358,9 +358,9 @@ func (m *ModuleRecordList) Fill(r *http.Request) (err error) {
post[name] = string(param[0])
}
if val, ok := get["query"]; ok {
if val, ok := get["filter"]; ok {
m.Query = val
m.Filter = val
}
if val, ok := get["page"]; ok {

View File

@@ -29,7 +29,7 @@ type (
FindByID(recordID uint64) (*types.Record, error)
Report(moduleID uint64, metrics, dimensions, filter string) (interface{}, error)
Find(moduleID uint64, query string, page int, perPage int, sort string) (*repository.FindResponse, error)
Find(moduleID uint64, filter string, sort string, page int, perPage int) (*repository.FindResponse, error)
Create(record *types.Record) (*types.Record, error)
Update(record *types.Record) (*types.Record, error)
@@ -68,8 +68,8 @@ func (s *record) Report(moduleID uint64, metrics, dimensions, filter string) (in
return s.repository.Report(moduleID, metrics, dimensions, filter)
}
func (s *record) Find(moduleID uint64, query string, page int, perPage int, sort string) (*repository.FindResponse, error) {
response, err := s.repository.Find(moduleID, query, page, perPage, sort)
func (s *record) Find(moduleID uint64, filter string, sort string, page int, perPage int) (*repository.FindResponse, error) {
response, err := s.repository.Find(moduleID, filter, sort, page, perPage)
if err != nil {
return nil, err
}

View File

@@ -182,7 +182,7 @@ func TestRecord(t *testing.T) {
// fetch all records
{
mr, err := repository.Find(module.ID, "", 0, 20, "id desc")
mr, err := repository.Find(module.ID, "", "id desc", 0, 20)
assert(t, err == nil, "Error when retrieving records: %+v", err)
assert(t, len(mr.Records) == 2, "Expected two record, got %d", len(mr.Records))
assert(t, mr.Meta.Count == 2, "Expected Meta.Count == 2, got %d", mr.Meta.Count)
@@ -193,7 +193,7 @@ func TestRecord(t *testing.T) {
// fetch all records
{
mr, err := repository.Find(module.ID, "", 0, 20, "name asc, email desc")
mr, err := repository.Find(module.ID, "", "name asc, email desc", 0, 20)
assert(t, err == nil, "Error when retrieving records: %+v", err)
assert(t, len(mr.Records) == 2, "Expected two record, got %d", len(mr.Records))
assert(t, mr.Meta.Count == 2, "Expected Meta.Count == 2, got %d", mr.Meta.Count)
@@ -204,7 +204,7 @@ func TestRecord(t *testing.T) {
// fetch all records
{
mr, err := repository.Find(module.ID, "", 0, 20, "created_at desc")
mr, err := repository.Find(module.ID, "", "created_at desc", 0, 20)
assert(t, err == nil, "Error when retrieving records: %+v", err)
assert(t, len(mr.Records) == 2, "Expected two record, got %d", len(mr.Records))
assert(t, mr.Meta.Count == 2, "Expected Meta.Count == 2, got %d", mr.Meta.Count)
@@ -215,19 +215,19 @@ func TestRecord(t *testing.T) {
// fetch all records by query
{
mr, err := repository.Find(module.ID, "petric", 0, 20, "id desc")
mr, err := repository.Find(module.ID, "name='Tit Petric'", "id desc", 0, 20)
assert(t, err == nil, "Error when retrieving records: %+v", err)
assert(t, len(mr.Records) == 1, "Expected one record, got %d", len(mr.Records))
assert(t, mr.Meta.Count == 1, "Expected Meta.Count == 1, got %d", mr.Meta.Count)
assert(t, mr.Meta.Page == 0, "Expected Meta.Page == 0, got %d", mr.Meta.Page)
assert(t, mr.Meta.PerPage == 20, "Expected Meta.PerPage == 20, got %d", mr.Meta.PerPage)
assert(t, mr.Meta.Query == "petric", "Expected Meta.Query == petric, got '%s'", mr.Meta.Query)
assert(t, mr.Meta.Filter == "name='Tit Petric'", "Expected Meta.Filter == name='Tit Petric', got '%s'", mr.Meta.Filter)
assert(t, mr.Meta.Sort == "id desc", "Expected Meta.Sort == id desc, got '%s'", mr.Meta.Sort)
}
// fetch all records by query
{
mr, err := repository.Find(module.ID, "niall", 0, 20, "id asc")
mr, err := repository.Find(module.ID, "niall", "id asc", 0, 20)
assert(t, err == nil, "Error when retrieving records: %+v", err)
assert(t, len(mr.Records) == 0, "Expected no records, got %d", len(mr.Records))
}
@@ -243,7 +243,7 @@ func TestRecord(t *testing.T) {
// fetch all records
{
mr, err := repository.Find(module.ID, "", 0, 20, "")
mr, err := repository.Find(module.ID, "", "", 0, 20)
assert(t, err == nil, "Error when retrieving records: %+v", err)
assert(t, len(mr.Records) == 0, "Expected no record, got %d", len(mr.Records))
}

View File

@@ -283,7 +283,7 @@ CRM module definitions
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| query | string | GET | Search query | N/A | NO |
| filter | string | GET | Filtering condition | N/A | NO |
| page | int | GET | Page number (0 based) | N/A | NO |
| perPage | int | GET | Returned items per page (default 50) | N/A | NO |
| sort | string | GET | Sort field (default id desc) | N/A | NO |