func SubTree(ctx context.Context, userID int, rootContent contenttype.ContentTyper, depth int, contentTypes string, sortby []string) (TreeNode, error) {
	contentTypeList := strings.Split(contentTypes, ",")
	var list []contenttype.ContentTyper
	for _, contentType := range contentTypeList {
		currentList, _, err := SubList(ctx, userID, rootContent, contentType, depth, db.EmptyCond().Sortby(sortby...))
		if err != nil {
			return TreeNode{}, err
		}
		for _, item := range currentList {
			list = append(list, item)
		}
	}
	treenode := TreeNode{Content: rootContent, Location: rootContent.GetLocation()}
	buildTree(&treenode, list)
	return treenode, nil
}
SubTree fetches content and return a tree result under rootContent, permission considered.
See TreeNode for the tree structure
package query
import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"github.com/digimakergo/digimaker/core/contenttype"
	"github.com/digimakergo/digimaker/core/db"
	"github.com/digimakergo/digimaker/core/definition"
	"github.com/digimakergo/digimaker/core/fieldtype"
	"github.com/digimakergo/digimaker/core/permission"
	"github.com/digimakergo/digimaker/core/util"
)
type TreeNode struct {
	*contenttype.Location
	Fields   interface{}              `json:"fields"`  
	Content  contenttype.ContentTyper `json:"content"`
	Children []TreeNode               `json:"children"`
}
func (tn *TreeNode) Iterate(operation func(node *TreeNode)) {
	operation(tn)
	for i, child := range tn.Children {
		child.Iterate(operation)
		tn.Children[i] = child
	}
}
func FetchByID(ctx context.Context, locationID int) (contenttype.ContentTyper, error) {
	location := contenttype.Location{}
	_, err := db.BindEntity(ctx, &location, "dm_location", db.Cond("id", locationID))
	if err != nil {
		return nil, fmt.Errorf("[contentquery.fetchbyid]Can not fetch location by locationID %v: %w", locationID, err)
	}
	if location.ID == 0 {
		return nil, errors.New("[contentquery.fetchbyid]Location is empty.")
	}
	contentID := location.ContentID
	contentType := location.ContentType
	result, err := FetchByCID(ctx, contentType, contentID)
	return result, err
}
func FetchByPath(ctx context.Context, path string) (contenttype.ContentTyper, error) {
	location := contenttype.Location{}
	_, err := db.BindEntity(ctx, &location, "dm_location", db.Cond("identifier_path", path))
	if err != nil {
		return nil, fmt.Errorf("[contentquery.fetchbypath]Can not fetch location by location path %v: %w", path, err)
	}
	if location.ID == 0 {
		return nil, errors.New("[contentquery.fetchbypath]Location is empty.")
	}
	contentID := location.ContentID
	contentType := location.ContentType
	result, err := FetchByCID(ctx, contentType, contentID)
	return result, err
}
func FetchByUID(ctx context.Context, uid string) (contenttype.ContentTyper, error) {
	location := contenttype.Location{}
	_, err := db.BindEntity(ctx, &location, "dm_location", db.Cond("uid", uid))
	if err != nil {
		return nil, fmt.Errorf("[contentquery.fetchbyuid]Can not fetch location by uid %v: %w", uid, err)
	}
	if location.ID == 0 {
		return nil, errors.New("[contentquery.fetchbyid]Location is empty.")
	}
	contentID := location.ContentID
	contentType := location.ContentType
	result, err := FetchByCID(ctx, contentType, contentID)
	return result, err
}
func FetchByCID(ctx context.Context, contentType string, contentID int) (contenttype.ContentTyper, error) {
	return Fetch(ctx, contentType, db.Cond("c.id", contentID))
}
func FetchByCUID(ctx context.Context, contentType string, cuid string) (contenttype.ContentTyper, error) {
	return Fetch(ctx, contentType, db.Cond("c.cuid", cuid))
}
func Fetch(ctx context.Context, contentType string, condition db.Condition) (contenttype.ContentTyper, error) {
	return DefaultQuerier.Fetch(ctx, contentType, condition)
}
func LocationList(condition db.Condition) ([]contenttype.Location, int, error) {
	locations := []contenttype.Location{}
	count, err := db.BindEntity(context.Background(), &locations, "dm_location", condition)
	if err != nil {
		return locations, 0, err
	}
	return locations, count, nil
}
func Children(ctx context.Context, userID int, parentContent contenttype.ContentTyper, contenttype string, cond db.Condition) ([]contenttype.ContentTyper, int, error) {
	contentTypeList := parentContent.Definition().AllowedTypes
	if !util.Contains(contentTypeList, contenttype) {
		return nil, -1, errors.New("content type " + contenttype + "doesn't exist or not allowed.")
	}
	result, countResult, err := SubList(ctx, userID, parentContent, contenttype, 1, cond)
	return result, countResult, err
}