func (s *Service) CreateSentence(i *Sentence) (*Sentence, error) {
	stmt, err := s.DB.PrepareNamed(insertSentence)
	if err != nil {
		return nil, errors.Wrap(err, "preparing insert sentence query")
	}
	defer stmt.Close()
	args := map[string]interface{}{
		"id":         uuid.New(),
		"documentID": i.DocumentID,
		"storeID":    i.StoreID,
		"body":       i.Body,
		"embedding":  i.Embedding,
		"context":    i.Context,
	}
	var r Sentence
	if err := stmt.Get(&r, args); err != nil {
		return nil, errors.Wrap(err, "insert sentence query")
	}
	return &r, nil
}
CreateSentence stores sentence and mapping to annoy index id from a parsed document
package document
import (
	"encoding/json"
	"fmt"
	"path/filepath"
	"strings"
	"time"
	"unicode"
	"github.com/jmoiron/sqlx"
	"github.com/lib/pq"
	"github.com/pborman/uuid"
	"github.com/pkg/errors"
)
const (
	TypeMarkdown = iota + 1
	TypePDF
	TypeUnsupported
	codeDuplicateInsert = pq.ErrorCode("23505")
	reservedFolderName = "_rootfolder_"
)
var (
	ErrDuplicateIndex = errors.New("index already exists")
	ErrDuplicateDocument = errors.New("document already exists")
)
type Type struct {
	ID      int       `json:"id" db:"document_type_id"`
	Name    string    `json:"name" db:"name"`
	Created time.Time `json:"created" db:"created"`
	Updated time.Time `json:"updated" db:"updated"`
}
type SearchResult struct {
	DocumentID   uuid.UUID `json:"id" db:"document_id"`
	SentenceID   uuid.UUID `json:"sentenceID" db:"sentence_id"`
	AnnoyID      int       `json:"annoyID" db:"annoy_id"`
	DocumentName string    `json:"name" db:"name"`
	Path         string    `json:"path" db:"path"`
	DownloadURL  string    `json:"downloadURL" db:"download_url"`
	Text         string    `json:"text" db:"sentence_text"`
	Rank         float32   `json:"rel" db:"rel"`
}
type Folder struct {
	FolderID uuid.UUID `json:"id" db:"folder_id"`
	Name     string    `json:"name" db:"name"`
	Path     string    `json:"path" db:"path"`
	Created  time.Time `json:"created" db:"created"`
	Updated  time.Time `json:"updated" db:"updated"`
}
type Document struct {
	ID               uuid.UUID `json:"id" db:"document_id"`
	FolderID         uuid.UUID `json:"folderID" db:"folder_id"`
	TypeID           int       `json:"typeID" db:"document_type_id"`
	ObjectStorageURL string    `json:"objectStorageURL" db:"object_storage_url"`
	DownloadURL      string    `json:"downloadURL" db:"download_url"`
	Path             string    `json:"path" db:"path"`
	Name             string    `json:"name" db:"name"`
	Body             string    `json:"body" db:"body"`
	Created          time.Time `json:"created" db:"created"`
	Updated          time.Time `json:"updated" db:"updated"`
}
type Sentence struct {
	ID         uuid.UUID       `db:"sentence_id"`
	DocumentID uuid.UUID       `db:"document_id"`
	StoreID    uuid.UUID       `db:"store_id"`
	AnnoyID    int             `db:"annoy_id"`
	Embedding  json.RawMessage `db:"embedding"`
	Body       string          `db:"body"`
	Context    string          `db:"context"`
	Created    time.Time       `json:"created" db:"created"`
	Updated    time.Time       `json:"updated" db:"updated"`
}
func (s *Sentence) GetEmbeddings() ([]float32, error) {
	var embeddings []float32
	if err := json.Unmarshal(s.Embedding, &embeddings); err != nil {
		return nil, errors.Wrap(err, "unmarshalling embedding")
	}
	return embeddings, nil
}
type Service struct {
	DB *sqlx.DB
}
func NewService(db *sqlx.DB) *Service {
	s := Service{
		DB: db,
	}
	return &s
}
func (s *Service) GetIndexContent() ([]Sentence, error) {
	var r []Sentence
	if err := s.DB.Select(&r, getIndexContent); err != nil {
		return nil, errors.Wrap(err, "get index content query")
	}
	return r, nil
}
func cleanPath(path string) string {
	path = strings.Replace(path, ".", "_", -1)  
	path = strings.Replace(path, "/", ".", -1)  
	cleaned := strings.Map(func(r rune) rune {
		if unicode.IsSpace(r) || r == '-' {
			return -1
		}
		return r
	}, path)
	return cleaned
}
func (s *Service) GetFolderByPath(path string) (*Folder, error) {
	stmt, err := s.DB.PrepareNamed(getFolderByPath)
	if err != nil {
		return nil, errors.Wrap(err, "preparing get folder by path query")
	}
	defer stmt.Close()
	args := map[string]interface{}{
		"path": cleanPath(path),
	}
	var r Folder
	if err := stmt.Get(&r, args); err != nil {
		return nil, errors.Wrap(err, "get folder by path query")
	}
	return &r, nil
}
func (s *Service) CreateFolderPath(path string, recursing bool) (string, error) {
	stmt, err := s.DB.PrepareNamed(insertFolder)
	if err != nil {
		return "", errors.Wrap(err, "preparing insert folder query")
	}
	defer stmt.Close()
	slashIndex := strings.LastIndex(path, "/")
	var name string
	if path == "/" {
		name = reservedFolderName
	}
	if (len(path) >= slashIndex+1) && name != reservedFolderName {
		name = path[slashIndex+1:]
	} else {
		name = reservedFolderName
	}
	if path == "/" {
		path = reservedFolderName
	} else {
		if !recursing {
			path = fmt.Sprintf("%v/%v", reservedFolderName, path)
		}
	}
	folderID := uuid.New()
	args := map[string]interface{}{
		"id":   folderID,
		"name": name,
		"path": cleanPath(path),
	}
	r, err := stmt.Exec(args)
	if err != nil {
		return "", errors.Wrap(err, "insert folder query")
	}
	a, err := r.RowsAffected()
	if err != nil {
		return "", errors.Wrap(err, "getting rows affected")
	}
	if a == 0 {
		f, err := s.GetFolderByPath(path)
		if err != nil {
			return "", errors.Wrap(err, "get folder by path")
		}
		folderID = f.FolderID.String()
		path = f.Path
	}
	parentPath := path
	if name != reservedFolderName {
		parentFolders := strings.Split(path, "/")
		for i := 0; i < len(parentFolders); i++ {
			slashIndex := strings.LastIndex(parentPath, "/")
			if slashIndex > -1 {
				parentPath = parentPath[:slashIndex]
			} else {
				parentPath = "/"  
			}
			_, err := s.CreateFolderPath(parentPath, true)
			if err != nil {
				return "", errors.Wrapf(err, "creating parent paths, err on path %v", parentPath)
			}
		}
	}
	return folderID, nil
}
func (s *Service) DeleteDocument(path string) error {
	stmt, err := s.DB.PrepareNamed(deleteDocumentByPath)
	if err != nil {
		return errors.Wrap(err, "preparing delete document by path query")
	}
	defer stmt.Close()
	args := map[string]interface{}{
		"path": cleanPath(path),
	}
	if _, err := stmt.Exec(args); err != nil {
		return errors.Wrap(err, "error deleting document")
	}
	return nil
}
func (s *Service) GetDocumentByPath(path string) (*Document, error) {
	stmt, err := s.DB.PrepareNamed(getDocumentByPath)
	if err != nil {
		return nil, errors.Wrap(err, "preparing get document by path query")
	}
	defer stmt.Close()
	filename := filepath.Base(path)
	if filename == "." {
		err := errors.New("a file was not defined in the given path")
		return nil, err
	}
	if path[0] != '/' {
		path = "/" + path
	}
	path = strings.Replace(path, "/", reservedFolderName, 1)
	path = strings.Replace(path, filename, "", 1)
	args := map[string]interface{}{
		"path": cleanPath(path) + "." + filename,
	}
	var r Document
	if err := stmt.Get(&r, args); err != nil {
		return nil, errors.Wrap(err, "get document by path query")
	}
	return &r, nil
}
func (s *Service) CreateDocument(d *Document) (*Document, error) {
	stmt, err := s.DB.PrepareNamed(insertDocument)
	if err != nil {
		return nil, errors.Wrap(err, "preparing insert document query")
	}
	defer stmt.Close()
	folderID, err := s.CreateFolderPath(d.Path, false)
	if err != nil {
		return nil, errors.Wrap(err, "creating folder for document")
	}
	path := reservedFolderName
	if d.Path != "/" {
		path = fmt.Sprintf("%v/%v", reservedFolderName, d.Path)
	}
	args := map[string]interface{}{
		"id":               uuid.New(),
		"typeID":           d.TypeID,
		"name":             d.Name,
		"body":             d.Body,
		"path":             cleanPath(path) + "." + d.Name,
		"objectStorageURL": d.ObjectStorageURL,
		"downloadURL":      d.DownloadURL,
		"folderID":         folderID,
	}
	var r Document
	if err := stmt.Get(&r, args); err != nil {
		if pgErr, ok := err.(*pq.Error); ok {
			if pgErr.Code == codeDuplicateInsert {
				return nil, ErrDuplicateDocument
			}
		}
		return nil, errors.Wrap(err, "insert document query")
	}
	return &r, nil
}