func (svc *Svc) ComponentFromReferenceSet(refset int64, component int64) ([]*snomed.ReferenceSetItem, error) {
	refsetID := make([]byte, 8)
	componentID := make([]byte, 8)
	binary.BigEndian.PutUint64(refsetID, uint64(refset))
	binary.BigEndian.PutUint64(componentID, uint64(component))
	key := compoundKey(refsetID, componentID)
	var result []*snomed.ReferenceSetItem
	return result, svc.store.View(func(batch Batch) error {
		values, err := batch.GetIndexEntries(ixReferenceSetComponentItems, key)
		if err != nil {
			return err
		}
		l := len(values)
		items := make([]snomed.ReferenceSetItem, l)
		result = make([]*snomed.ReferenceSetItem, l)
		for i, v := range values {
			if err := batch.Get(bkRefsetItems, v, &items[i]); err != nil {
				fmt.Fprintf(os.Stderr, "error fetching refset item with identifier %s: %s", v, err)
				return err
			}
			result[i] = &items[i]
		}
		return nil
	})
}
ComponentFromReferenceSet gets the specified components from the specified refset, or error
package terminology
import (
	"context"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"sync"
	"sync/atomic"
	"time"
	"github.com/wardle/go-terminology/snomed"
	"golang.org/x/text/language"
	"google.golang.org/protobuf/proto"
)
const (
	descriptorName = "sctdb.json"
	currentVersion = 4
	storeKind      = "level"
	searchKind     = "bleve"
)
type Svc struct {
	path   string
	store  Store
	search Search
	Descriptor
	availableLanguages []language.Tag
}
type Descriptor struct {
	Version    int32
	StoreKind  string
	SearchKind string
}
func NewService(path string, readOnly bool) (*Svc, error) {
	err := os.MkdirAll(path, 0771)
	if err != nil {
		return nil, err
	}
	descriptor, err := createOrOpenDescriptor(path, storeKind, searchKind)
	if err != nil {
		return nil, err
	}
	if descriptor.Version != currentVersion {
		return nil, fmt.Errorf("incompatible database format v%d, needed v%d", descriptor.Version, currentVersion)
	}
	if descriptor.StoreKind != storeKind {
		return nil, fmt.Errorf("incompatible database format '%s', needed %s", descriptor.StoreKind, storeKind)
	}
	if descriptor.SearchKind != searchKind {
		return nil, fmt.Errorf("incompatible database format '%s', needed %s", descriptor.SearchKind, searchKind)
	}
	store, err := newLevelService(filepath.Join(path, "level.db"), readOnly)
	if err != nil {
		return nil, err
	}
	bleve, err := newBleveIndex(filepath.Join(path, "bleve.db"), readOnly)
	if err != nil {
		return nil, err
	}
	svc := &Svc{path: path, store: store, search: bleve, Descriptor: *descriptor}
	if svc.availableLanguages, err = svc.AvailableLanguages(); err != nil {
		return nil, err
	}
	return svc, nil
}
func (svc *Svc) Close() error {
	if svc.search != nil {
		if err := svc.search.Close(); err != nil {
			return err
		}
	}
	return svc.store.Close()
}
func createOrOpenDescriptor(path string, storeKind string, searchKind string) (*Descriptor, error) {
	descriptorFilename := filepath.Join(path, descriptorName)
	if _, err := os.Stat(descriptorFilename); os.IsNotExist(err) {
		desc := &Descriptor{
			Version:    currentVersion,
			StoreKind:  storeKind,
			SearchKind: searchKind,
		}
		return desc, saveDescriptor(path, desc)
	}
	data, err := ioutil.ReadFile(descriptorFilename)
	if err != nil {
		return nil, err
	}
	var desc Descriptor
	return &desc, json.Unmarshal(data, &desc)
}
func saveDescriptor(path string, descriptor *Descriptor) error {
	descriptorFilename := filepath.Join(path, descriptorName)
	data, err := json.Marshal(descriptor)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(descriptorFilename, data, 0644)
}
func (svc *Svc) Put(context context.Context, components interface{}) error {
	var err error
	switch components := components.(type) {
	case []*snomed.Concept:
		err = svc.putConcepts(components)
	case []*snomed.Description:
		err = svc.putDescriptions(components)
	case []*snomed.Relationship:
		err = svc.putRelationships(components)
	case []*snomed.ReferenceSetItem:
		err = svc.putReferenceSets(components)
	default:
		err = fmt.Errorf("unknown component type: %T", components)
	}
	return err
}
func (svc *Svc) Concept(conceptID int64) (*snomed.Concept, error) {
	key := make([]byte, 8)
	binary.BigEndian.PutUint64(key, uint64(conceptID))
	var c snomed.Concept
	return &c, svc.store.View(func(batch Batch) error {
		return batch.Get(bkConcepts, key, &c)
	})
}
func (svc *Svc) Concepts(conceptIDs ...int64) ([]*snomed.Concept, error) {
	key := make([]byte, 8)
	l := len(conceptIDs)
	r1 := make([]snomed.Concept, l)
	r2 := make([]*snomed.Concept, l)
	err := svc.store.View(func(batch Batch) error {
		for i, id := range conceptIDs {
			binary.BigEndian.PutUint64(key, uint64(id))
			if err := batch.Get(bkConcepts, key, &r1[i]); err != nil {
				return err
			}
			r2[i] = &r1[i]
		}
		return nil
	})
	return r2, err
}
func (svc *Svc) putConcepts(concepts []*snomed.Concept) error {
	key := make([]byte, 8)
	var existing snomed.Concept
	return svc.store.Update(func(batch Batch) error {
		for _, c := range concepts {
			binary.BigEndian.PutUint64(key, uint64(c.Id))
			err := batch.Get(bkConcepts, key, &existing)
			if err == ErrNotFound {
				batch.Put(bkConcepts, key, c)
			} else {
				nt := c.EffectiveTime.AsTime()
				ot := existing.EffectiveTime.AsTime()
				if nt.After(ot) {
					batch.Put(bkConcepts, key, c)
				}
			}
		}
		return nil
	})
}
func (svc *Svc) putDescriptions(descriptions []*snomed.Description) error {
	dID := make([]byte, 8)
	var existing snomed.Description
	return svc.store.Update(func(batch Batch) error {
		for _, d := range descriptions {
			binary.BigEndian.PutUint64(dID, uint64(d.Id))
			err := batch.Get(bkDescriptions, dID, &existing)
			if err == ErrNotFound {
				batch.Put(bkDescriptions, dID, d)
			} else {
				nt := d.EffectiveTime.AsTime()
				ot := existing.EffectiveTime.AsTime()
				if nt.After(ot) {
					batch.Put(bkDescriptions, dID, d)
				}
			}
		}
		return nil
	})
}
func (svc *Svc) indexDescriptions(batch Batch, ds []*snomed.Description) {
	cID := make([]byte, 8)
	dID := make([]byte, 8)
	for _, d := range ds {
		binary.BigEndian.PutUint64(cID, uint64(d.ConceptId))
		binary.BigEndian.PutUint64(dID, uint64(d.Id))
		batch.AddIndexEntry(ixConceptDescriptions, cID, dID)
	}
}
func (svc *Svc) Description(descriptionID int64) (*snomed.Description, error) {
	key := make([]byte, 8)
	binary.BigEndian.PutUint64(key, uint64(descriptionID))
	var c snomed.Description
	return &c, svc.store.View(func(batch Batch) error {
		return batch.Get(bkDescriptions, key, &c)
	})
}
func (svc *Svc) descriptionIDs(conceptID int64) (result []int64, err error) {
	key := make([]byte, 8)
	binary.BigEndian.PutUint64(key, uint64(conceptID))
	svc.store.View(func(batch Batch) error {
		values, err := batch.GetIndexEntries(ixConceptDescriptions, key)
		if err != nil {
			return err
		}
		l := len(values)
		result = make([]int64, l)
		for i, v := range values {
			result[i] = int64(binary.BigEndian.Uint64(v))
		}
		return nil
	})
	return
}
func (svc *Svc) Descriptions(conceptID int64) (result []*snomed.Description, err error) {
	key := make([]byte, 8)
	binary.BigEndian.PutUint64(key, uint64(conceptID))
	svc.store.View(func(batch Batch) error {
		values, err := batch.GetIndexEntries(ixConceptDescriptions, key)
		if err != nil {
			return err
		}
		l := len(values)
		descs := make([]snomed.Description, l)
		result = make([]*snomed.Description, l)
		for i, v := range values {
			if err := batch.Get(bkDescriptions, v, &descs[i]); err != nil {
				return err
			}
			result[i] = &descs[i]
		}
		return nil
	})
	return
}
func (svc *Svc) putRelationships(relationships []*snomed.Relationship) error {
	rID := make([]byte, 8)
	var existing snomed.Relationship
	return svc.store.Update(func(batch Batch) error {
		for _, r := range relationships {
			binary.BigEndian.PutUint64(rID, uint64(r.Id))
			err := batch.Get(bkRelationships, rID, &existing)
			if err == ErrNotFound {
				batch.Put(bkRelationships, rID, r)
			} else {
				nt := r.EffectiveTime.AsTime()
				ot := existing.EffectiveTime.AsTime()
				if nt.After(ot) {
					batch.Put(bkRelationships, rID, r)
				}
			}
		}
		return nil
	})
}
func (svc *Svc) indexRelationships(batch Batch, rs []*snomed.Relationship) {
	rID := make([]byte, 8)
	sourceID := make([]byte, 8)
	destinationID := make([]byte, 8)
	for _, r := range rs {
		binary.BigEndian.PutUint64(rID, uint64(r.Id))
		binary.BigEndian.PutUint64(sourceID, uint64(r.SourceId))
		binary.BigEndian.PutUint64(destinationID, uint64(r.DestinationId))
		batch.AddIndexEntry(ixConceptParentRelationships, sourceID, rID)
		batch.AddIndexEntry(ixConceptChildRelationships, destinationID, rID)
		if r.TypeId == snomed.IsA && r.Active {
			batch.AddIndexEntry(ixConceptParents, sourceID, destinationID)
			batch.AddIndexEntry(ixConceptChildren, destinationID, sourceID)
		}
	}
}
func (svc *Svc) ChildRelationships(conceptID int64) ([]*snomed.Relationship, error) {
	return svc.getRelationships(conceptID, ixConceptChildRelationships)
}
func (svc *Svc) ParentRelationships(conceptID int64) ([]*snomed.Relationship, error) {
	return svc.getRelationships(conceptID, ixConceptParentRelationships)
}
func (svc *Svc) getRelationships(conceptID int64, idx bucket) ([]*snomed.Relationship, error) {
	key := make([]byte, 8)
	binary.BigEndian.PutUint64(key, uint64(conceptID))
	var result []*snomed.Relationship
	return result, svc.store.View(func(batch Batch) error {
		entries, err := batch.GetIndexEntries(idx, key)
		if err != nil {
			return err
		}
		l := len(entries)
		relationships := make([]snomed.Relationship, l)
		result = make([]*snomed.Relationship, l)
		for i, id := range entries {
			if err := batch.Get(bkRelationships, id, &relationships[i]); err != nil {
				return err
			}
			result[i] = &relationships[i]
		}
		return nil
	})
}
func (svc *Svc) ReferenceSetItem(itemID string) (*snomed.ReferenceSetItem, error) {
	var item snomed.ReferenceSetItem
	return &item, svc.store.View(func(batch Batch) error {
		return batch.Get(bkRefsetItems, []byte(itemID), &item)
	})
}
func (svc *Svc) putReferenceSets(refset []*snomed.ReferenceSetItem) error {
	var existing snomed.ReferenceSetItem
	return svc.store.Update(func(batch Batch) error {
		for _, item := range refset {
			itemID := []byte(item.Id)
			err := batch.Get(bkRefsetItems, itemID, &existing)
			if err == ErrNotFound {
				batch.Put(bkRefsetItems, itemID, item)
			} else {
				nt := item.EffectiveTime.AsTime()
				ot := existing.EffectiveTime.AsTime()
				if nt.After(ot) {
					batch.Put(bkRefsetItems, itemID, item)
				}
			}
		}
		return nil
	})
}
func (svc *Svc) indexRefsetItems(batch Batch, rs []*snomed.ReferenceSetItem) {
	refsetID := make([]byte, 8)
	referencedComponentID := make([]byte, 8)
	for _, r := range rs {
		itemID := []byte(r.Id)
		binary.BigEndian.PutUint64(referencedComponentID, uint64(r.ReferencedComponentId))
		binary.BigEndian.PutUint64(refsetID, uint64(r.RefsetId))
		batch.AddIndexEntry(ixComponentReferenceSets, referencedComponentID, refsetID)
		batch.AddIndexEntry(ixReferenceSetComponentItems, compoundKey(refsetID, referencedComponentID), itemID)
		var target string
		if simpleMap := r.GetSimpleMap(); simpleMap != nil {
			target = simpleMap.GetMapTarget()
		} else if complexMap := r.GetComplexMap(); complexMap != nil {
			target = complexMap.GetMapTarget()
		}
		if target != "" {
			batch.AddIndexEntry(ixRefsetTargetItems, compoundKey(refsetID, []byte(target+" ")), itemID)
		}
		batch.AddIndexEntry(ixReferenceSets, refsetID, nil)
	}
}
func (svc *Svc) ComponentReferenceSets(referencedComponentID int64) ([]int64, error) {
	key := make([]byte, 8)
	binary.BigEndian.PutUint64(key, uint64(referencedComponentID))
	var result []int64
	return result, svc.store.View(func(batch Batch) error {
		values, err := batch.GetIndexEntries(ixComponentReferenceSets, key)
		if err != nil {
			return err
		}
		result = make([]int64, len(values))
		for i, v := range values {
			result[i] = int64(binary.BigEndian.Uint64(v))
		}
		return nil
	})
}
func (svc *Svc) IsInReferenceSet(referencedComponentID int64, refsetID int64) (result bool, err error) {
	k := make([]byte, 8)
	v := make([]byte, 8)
	binary.BigEndian.PutUint64(k, uint64(referencedComponentID))
	binary.BigEndian.PutUint64(v, uint64(refsetID))
	err = svc.store.View(func(batch Batch) error {
		result, err = batch.CheckIndexEntry(ixComponentReferenceSets, k, v)
		return nil
	})
	return
}
func (svc *Svc) MapTarget(refset int64, target string) ([]*snomed.ReferenceSetItem, error) {
	refsetID := make([]byte, 8)
	binary.BigEndian.PutUint64(refsetID, uint64(refset))
	key := compoundKey(refsetID, []byte(target+" "))  
	var result []*snomed.ReferenceSetItem
	err := svc.store.View(func(batch Batch) error {
		values, err := batch.GetIndexEntries(ixRefsetTargetItems, key)
		if err != nil {
			return err
		}
		l := len(values)
		items := make([]snomed.ReferenceSetItem, l)
		result = make([]*snomed.ReferenceSetItem, l)
		for i, v := range values {
			if err := batch.Get(bkRefsetItems, v, &items[i]); err != nil {
				return err
			}
			result[i] = &items[i]
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	sort.Slice(result, func(i, j int) bool {
		var gi, gj int64 = -1, -1  
		var pi, pj int64 = -1, -1  
		if cmi := result[i].GetComplexMap(); cmi != nil {
			gi = cmi.GetMapGroup()
			pi = cmi.GetMapPriority()
		}
		if cmj := result[j].GetComplexMap(); cmj != nil {
			gj = cmj.GetMapGroup()
			pj = cmj.GetMapPriority()
		}
		if gi != gj {
			return gi < gj
		}
		return pi < pj
	})
	return result, nil
}
func (svc *Svc) ReferenceSetComponents(refset int64) (map[int64]struct{}, error) {
	result := make(map[int64]struct{})
	key := make([]byte, 8)
	binary.BigEndian.PutUint64(key, uint64(refset))
	return result, svc.store.View(func(batch Batch) error {
		values, err := batch.GetIndexEntries(ixReferenceSetComponentItems, key)
		if err != nil {
			return err
		}
		for _, v := range values {
			result[int64(binary.BigEndian.Uint64(v[:8]))] = struct{}{}
		}
		return nil
	})
}