Exemple #1
0
func TestCommand(t *testing.T) {
	for _, testTable := range []struct {
		cmd      Command
		expected string
	}{
		{
			cmd: Command{
				Database:  "name.idx",
				Index:     "empresas",
				Command:   "mergeset",
				Key:       []byte("teste"),
				KeyType:   TypeString,
				Value:     utils.Uint64ToBytes(1000),
				ValueType: TypeUint,
			},
			expected: `USING empresas.name.idx MERGESET 'teste' uint(1000);`,
		},
		{
			cmd: Command{
				Database:  "name.idx",
				Index:     "empresas",
				Command:   "batch",
				Key:       nil,
				KeyType:   TypeNil,
				Value:     nil,
				ValueType: TypeNil,
			},
			expected: `USING empresas.name.idx BATCH;`,
		},
		{
			cmd: Command{
				Database:  "name.idx",
				Index:     "empresas",
				Command:   "get",
				Key:       []byte("teste"),
				KeyType:   TypeString,
				Value:     nil,
				ValueType: TypeNil,
			},
			expected: `USING empresas.name.idx GET 'teste';`,
		},
	} {
		cmdRev := testTable.cmd.Reverse()

		if cmdRev != testTable.expected {
			t.Errorf("Differs: '%s' !== '%s'", cmdRev, testTable.expected)
		}
	}
}
func TestBuildAddObjectDocument(t *testing.T) {
	var (
		indexName                  = "document-with-object-sample"
		indexDir                   = DataDirTmp + "/" + indexName
		commands, expectedCommands []engine.Command
		docJSON                    = []byte(`
                {
                    "id": 1,
                    "address": {
                        "city": "florianópolis",
                        "district": "Itacorubi",
                        "street": "Patricio Farias",
                        "latlon": [
                            -27.545198,
                            -48.504827
                        ]
                    }
                }`)
		err   error
		index *Index
	)

	cfg := Config{
		Debug:   false,
		DataDir: DataDirTmp,
	}

	err = os.MkdirAll(DataDirTmp, 0755)

	if err != nil {
		goto cleanup
	}

	index, err = New(indexName, cfg, true)

	if err != nil {
		t.Error(err)
		goto cleanup
	}

	if _, err := os.Stat(indexDir); os.IsNotExist(err) {
		t.Errorf("no such file or directory: %s", indexDir)
		goto cleanup
	}

	commands, err = index.BuildAdd(1, docJSON, nil)

	if err != nil {
		t.Error(err.Error())
		goto cleanup
	}

	expectedCommands = []engine.Command{
		{
			Index:     indexName,
			Database:  "document.db",
			Key:       utils.Uint64ToBytes(1),
			KeyType:   engine.TypeUint,
			Value:     docJSON,
			ValueType: engine.TypeString,
			Command:   "set",
		},
		{
			Index:     indexName,
			Database:  "address.city_string.idx",
			Key:       []byte("florianópolis"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(1),
			ValueType: engine.TypeUint,
			Command:   "mergeset",
		},
		{
			Index:     indexName,
			Database:  "address.district_string.idx",
			Key:       []byte("itacorubi"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(1),
			ValueType: engine.TypeUint,
			Command:   "mergeset",
		},
		{
			Index:     indexName,
			Database:  "address.latlon_float.idx",
			Key:       utils.Float64ToBytes(-27.545198),
			KeyType:   engine.TypeFloat,
			Value:     utils.Uint64ToBytes(1),
			ValueType: engine.TypeUint,
			Command:   "mergeset",
		},
		{
			Index:     indexName,
			Database:  "address.latlon_float.idx",
			Key:       utils.Float64ToBytes(-48.504827),
			KeyType:   engine.TypeFloat,
			Value:     utils.Uint64ToBytes(1),
			ValueType: engine.TypeUint,
			Command:   "mergeset",
		},
		{
			Index:     indexName,
			Database:  "address.street_string.idx",
			Key:       []byte("patricio"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(1),
			ValueType: engine.TypeUint,
			Command:   "mergeset",
		},
		{
			Index:     indexName,
			Database:  "address.street_string.idx",
			Key:       []byte("farias"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(1),
			ValueType: engine.TypeUint,
			Command:   "mergeset",
		},
		{
			Index:     indexName,
			Database:  "address.street_string.idx",
			Key:       []byte("patricio farias"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(1),
			ValueType: engine.TypeUint,
			Command:   "mergeset",
		},
		{
			Index:     indexName,
			Database:  "id_float.idx",
			Key:       utils.Float64ToBytes(1),
			KeyType:   engine.TypeFloat,
			Value:     utils.Uint64ToBytes(1),
			ValueType: engine.TypeUint,
			Command:   "mergeset",
		},
	}

	if !compareCommands(t, commands, expectedCommands) {
		goto cleanup
	}

cleanup:
	index.Close()
	os.RemoveAll(indexDir)
}
Exemple #3
0
// TestEngineIntegerKeyOrder verifies if the chosen storage engine is really
// a LSM database ordered by key with ByteWise comparator.
func TestEngineIntegerKeyOrder(t *testing.T) {
	ng := New(NGConfig{
		KVCfg: &store.KVConfig{
			DataDir: DataDirTmp,
		},
		OpenCacheSize: 1,
	})

	cmds := []Command{
		{
			Index:    "sample",
			Database: "test.idx",
			Key:      []byte("AAA"),
			Value:    []byte("value AAA"),
			Command:  "set",
		},
		{
			Index:    "sample",
			Database: "test.idx",
			Key:      utils.Uint64ToBytes(1),
			Value:    []byte("value 1"),
			Command:  "set",
		},
		{
			Index:    "sample",
			Database: "test.idx",
			Key:      []byte("BBB"),
			Value:    []byte("value BBB"),
			Command:  "set",
		},
		{
			Index:    "sample",
			Database: "test.idx",
			Key:      utils.Uint64ToBytes(2),
			Value:    []byte("value 2"),
			Command:  "set",
		},
		{
			Index:    "sample",
			Database: "test.idx",
			Key:      utils.Uint64ToBytes(2000),
			Value:    []byte("value 2000"),
			Command:  "set",
		},
		{
			Index:    "sample",
			Database: "test.idx",
			Key:      []byte("2000"),
			Value:    []byte("value 2000"),
			Command:  "set",
		},
		{
			Index:    "sample",
			Database: "test.idx",
			Key:      utils.Uint64ToBytes(100000),
			Value:    []byte("value 100000"),
			Command:  "set",
		},
		{
			Index:    "sample",
			Database: "test.idx",
			Key:      utils.Uint64ToBytes(1000000),
			Value:    []byte("value 1000000"),
			Command:  "set",
		},
		{
			Index:    "sample",
			Database: "test.idx",
			Key:      utils.Uint64ToBytes(10000000000000),
			Value:    []byte("value 10000000000000"),
			Command:  "set",
		},
	}

	execSequence(t, ng, cmds)

	itReturns := []map[int64]string{
		{
			1: "value 1",
		},
		{
			2: "value 2",
		},
		{
			2000: "value 2000",
		},
		{
			100000: "value 100000",
		},
		{
			1000000: "value 1000000",
		},
		{
			10000000000000: "value 10000000000000",
		},
	}

	cmpIterator(t, itReturns, ng, utils.Uint64ToBytes(1), "sample", "test.idx")

	defer ng.Close()
	os.RemoveAll(DataDirTmp)
}
Exemple #4
0
func TestBuildAddDocument(t *testing.T) {
	var (
		indexName                  = "document-sample"
		commands, expectedCommands []engine.Command
		docJSON                    = []byte(`{"id": 1}`)
		err                        error
		index                      *Index
		indexDir                   = DataDirTmp + "/" + indexName
	)

	index, err = createIndex(indexName, t)

	if err != nil {
		t.Error(err)
		return
	}

	commands, err = index.BuildAdd(1, docJSON, nil)

	if err != nil {
		t.Error(err.Error())
		goto cleanup
	}

	expectedCommands = []engine.Command{
		{
			Index:     indexName,
			Database:  "document.db",
			Key:       utils.Uint64ToBytes(1),
			KeyType:   engine.TypeUint,
			Value:     docJSON,
			ValueType: engine.TypeString,
			Command:   "set",
		},
		{
			Index:     indexName,
			Database:  "id_float.idx",
			Key:       utils.Float64ToBytes(1),
			KeyType:   engine.TypeFloat,
			Value:     utils.Uint64ToBytes(1),
			ValueType: engine.TypeUint,
			Command:   "mergeset",
		},
	}

	if !compareCommands(t, commands, expectedCommands) {
		goto cleanup
	}

	docJSON = []byte(`{
            "title": "NeoSearch - Reverse Index",
            "description": "Neoway Full Text Search"
        }`)

	expectedCommands = []engine.Command{
		{
			Index:     indexName,
			Database:  "document.db",
			Command:   "set",
			Key:       utils.Uint64ToBytes(2),
			KeyType:   engine.TypeUint,
			Value:     docJSON,
			ValueType: engine.TypeString,
		},
		{
			Index:     indexName,
			Database:  "description_string.idx",
			Command:   "mergeset",
			Key:       []byte("neoway"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "description_string.idx",
			Command:   "mergeset",
			Key:       []byte("full"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "description_string.idx",
			Command:   "mergeset",
			Key:       []byte("text"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "description_string.idx",
			Command:   "mergeset",
			Key:       []byte("search"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "description_string.idx",
			Command:   "mergeset",
			Key:       []byte("neoway full text search"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "title_string.idx",
			Command:   "mergeset",
			Key:       []byte("neosearch"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "title_string.idx",
			Command:   "mergeset",
			Key:       []byte("-"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "title_string.idx",
			Command:   "mergeset",
			Key:       []byte("reverse"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "title_string.idx",
			Command:   "mergeset",
			Key:       []byte("index"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "title_string.idx",
			Command:   "mergeset",
			Key:       []byte("neosearch - reverse index"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
	}

	commands, err = index.BuildAdd(2, docJSON, nil)

	if err != nil {
		t.Error(err)
		goto cleanup
	}

	if !compareCommands(t, commands, expectedCommands) {
		goto cleanup
	}

cleanup:
	index.Close()
	os.RemoveAll(indexDir)
}
Exemple #5
0
func TestCliParserFromReader(t *testing.T) {
	commands := []engine.Command{}
	error := FromReader(strings.NewReader("using sample.TEST mergeset a 1;"), &commands)

	if error != nil {
		t.Error(error)
	}

	compareCommand(commands[0], engine.Command{
		Index:     "sample",
		Database:  "TEST",
		Command:   "mergeset",
		Key:       []byte("a"),
		KeyType:   engine.TypeString,
		Value:     utils.Uint64ToBytes(1),
		ValueType: engine.TypeUint,
	}, t)

	compareArray(`using sample.test.idx mergeset a 2;
             using sample.document.db set 1 "{id: 1, name: \"teste\"}";
             using sample.lalala set hello "world";
             using sample.mimimi get hello;
             using sample.lelele delete "teste";
             using sample.bleh.idx get uint(1);
             using sample.aaaa.bbb set uint(10000) int(10);
             using sample.bbbb.ccc mergeset "hellooooooooooooooooo" uint(102999299112211223);
             using sample.aaa delete "bbb"
        `, []engine.Command{
		engine.Command{
			Index:     "sample",
			Database:  "test.idx",
			Command:   "mergeset",
			Key:       []byte("a"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		engine.Command{
			Index:     "sample",
			Database:  "document.db",
			Command:   "set",
			Key:       utils.Int64ToBytes(1),
			KeyType:   engine.TypeInt,
			Value:     []byte("{id: 1, name: \"teste\"}"),
			ValueType: engine.TypeString,
		},
		engine.Command{
			Index:     "sample",
			Database:  "lalala",
			Command:   "set",
			Key:       []byte("hello"),
			KeyType:   engine.TypeString,
			Value:     []byte("world"),
			ValueType: engine.TypeString,
		},
		engine.Command{
			Index:    "sample",
			Database: "mimimi",
			Command:  "get",
			Key:      []byte("hello"),
			KeyType:  engine.TypeString,
		},
		engine.Command{
			Index:    "sample",
			Database: "lelele",
			Command:  "delete",
			Key:      []byte("teste"),
			KeyType:  engine.TypeString,
		},
		engine.Command{
			Index:    "sample",
			Database: "bleh.idx",
			Command:  "get",
			Key:      utils.Uint64ToBytes(1),
			KeyType:  engine.TypeUint,
		},
		engine.Command{
			Index:     "sample",
			Database:  "aaaa.bbb",
			Command:   "set",
			Key:       utils.Uint64ToBytes(10000),
			KeyType:   engine.TypeUint,
			Value:     utils.Int64ToBytes(10),
			ValueType: engine.TypeInt,
		},
		engine.Command{
			Index:     "sample",
			Database:  "bbbb.ccc",
			Command:   "mergeset",
			Key:       []byte("hellooooooooooooooooo"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(102999299112211223),
			ValueType: engine.TypeUint,
		},
		engine.Command{
			Index:    "sample",
			Database: "aaa",
			Command:  "delete",
			Key:      []byte("bbb"),
			KeyType:  engine.TypeString,
		},
	}, t)

	// underscore in the index name should pass
	commands = []engine.Command{}
	error = FromReader(strings.NewReader(`using sample.user_password set admin "s3cr3t"`),
		&commands)

	if error != nil {
		t.Error(error)
	}

	compareCommand(commands[0], engine.Command{
		Index:     "sample",
		Database:  "user_password",
		Command:   "set",
		Key:       []byte("admin"),
		KeyType:   engine.TypeString,
		Value:     []byte("s3cr3t"),
		ValueType: engine.TypeString,
	}, t)

	// invalid keyword "usinga"
	shouldThrowError(`
             usinga sample.test.idx set "hello" "world";
        `, t)

}
Exemple #6
0
// FromReader parse the file
func FromReader(file io.Reader, listCommands *[]engine.Command) error {
	var command engine.Command

	pState := parserState{}

	// Create our lexer
	// NewSize(startState, reader, readerBufLen, channelCap)
	lex := lexer.NewSize(lexFunc, file, 100, 1)
	var lastTokenType = TokenNil

	// Process lexer-emitted tokens
	for t := lex.NextToken(); lexer.TokenTypeEOF != t.Type(); t = lex.NextToken() {
		switch t.Type() {
		case TokenWord:
			if lastTokenType != TokenWord {
				tokenValue := string(t.Bytes())

				// TokenWord is a double or single quoted string?
				if pState.IsDoubleQuotedString || pState.IsSingleQuotedString {
					if !pState.IsUsing && !pState.IsCommand && !pState.IsValue {
						return errors.New("Invalid quoted string: " + tokenValue)
					}

					setQuotedString(tokenValue, &command, &pState)

					// TokenWord is the Index name?
					// using <TokenWord> ...
				} else if pState.IsUsing {
					indexDbParts := strings.Split(tokenValue, ".")

					if len(indexDbParts) < 2 {
						return fmt.Errorf("Invalid USING <index>.<database>: %s", tokenValue)
					}

					command.Index = indexDbParts[0]
					command.Database = strings.Join(indexDbParts[1:], ".")
					pState.IsUsing = false

					// TokenWord is the key of command?
					// using document.db mergeset <TokenWord> ...
				} else if pState.IsCommand {
					if strings.HasPrefix(tokenValue, "int(") {
						pState.KVType = engine.TypeInt
						pState.IsCastOpen = true
					} else if strings.HasPrefix(tokenValue, "uint(") {
						pState.KVType = engine.TypeUint
						pState.IsCastOpen = true
					} else if strings.HasPrefix(tokenValue, "float(") {
						pState.KVType = engine.TypeFloat
						pState.IsCastOpen = true
					} else {
						command.Key = []byte(tokenValue)
						command.KeyType = engine.TypeString
						pState.IsCommand = false
						pState.IsValue = true
					}

					// TokenWord is the command value?
					// using document.db mergeset name <TokenWord>
				} else if pState.IsValue {
					if tokenValue == ")" && pState.IsCastOpen {
						pState.IsCastOpen = false
					} else if strings.HasPrefix(tokenValue, "uint(") {
						pState.IsCastOpen = true
						pState.KVType = engine.TypeUint
					} else if strings.HasPrefix(tokenValue, "int(") {
						pState.IsCastOpen = true
						pState.KVType = engine.TypeInt
					} else if strings.HasPrefix(tokenValue, "float(") {
						pState.IsCastOpen = true
						pState.KVType = engine.TypeFloat
					} else {
						command.Value = []byte(tokenValue)
						command.ValueType = engine.TypeString
						pState.IsValue = false
					}
				} else {
					// Here we handle the available KEYWORDS

					if pState.IsCastOpen && strings.HasPrefix(tokenValue, ")") {
						pState.IsCastOpen = false
						pState.KVType = 0

						// Keyword USING
					} else if !pState.IsUsing && strings.ToLower(tokenValue) == "using" {
						pState.IsUsing = true
					} else if !pState.IsCommand {
						// Must be a command KEYWORD
						// see commandsAvailable

						if !isValidCommand(tokenValue) {
							return fmt.Errorf("Invalid keyword '"+tokenValue+"': %s", command)
						}

						pState.IsCommand = true
						pState.IsUsing = false
						command.Command = strings.ToLower(tokenValue)
					}
				}
			}
		case TokenDoubleQuotedString:
			if pState.IsDoubleQuotedString {
				if pState.IsCommand {
					pState.IsCommand = false
					pState.IsValue = true
				} else if pState.IsUsing {
					pState.IsUsing = false
				} else if pState.IsValue {
					pState.IsValue = false
				}
			}

			if pState.IsSingleQuotedString {
				setQuotedString(string(t.Bytes()), &command, &pState)
			} else {
				pState.IsDoubleQuotedString = !pState.IsDoubleQuotedString
			}
		case TokenSingleQuotedString:
			if pState.IsSingleQuotedString {
				if pState.IsCommand {
					pState.IsCommand = false
					pState.IsValue = true
				} else if pState.IsUsing {
					pState.IsUsing = false
				} else if pState.IsValue {
					pState.IsValue = false
				}
			}

			if pState.IsDoubleQuotedString {
				setQuotedString(string(t.Bytes()), &command, &pState)
			} else {
				pState.IsSingleQuotedString = !pState.IsSingleQuotedString
			}

		case TokenEscapedDoubleQuotedString:
			if pState.IsSingleQuotedString {
				panic("Escaped double quoted string inside single quoted string...")
			} else if pState.IsDoubleQuotedString {
				setQuotedString(string(t.Bytes()[1:]), &command, &pState)
			}

			pState.IsEscapedDoubleQuotedString = !pState.IsEscapedDoubleQuotedString

		case TokenEscapedSingleQuotedString:
			if pState.IsDoubleQuotedString {
				return errors.New("Escaped single quoted string inside double quoted string...")
			} else if pState.IsSingleQuotedString {
				setQuotedString(string(t.Bytes()), &command, &pState)
			}
		case TokenSpace:
			// Spaces only makes difference inside quotes
			if pState.IsDoubleQuotedString || pState.IsSingleQuotedString {
				setQuotedString(string(t.Bytes()), &command, &pState)
			}

		case TokenNewline:
			// New lines only makes difference inside quotes
			if pState.IsDoubleQuotedString || pState.IsSingleQuotedString {
				setQuotedString(string(t.Bytes()), &command, &pState)
			}
		case TokenSemiColon:
			if pState.IsSingleQuotedString || pState.IsDoubleQuotedString {
				setQuotedString(string(t.Bytes()), &command, &pState)
			} else {
				*listCommands = append(*listCommands, command)
				command = engine.Command{}
				pState = parserState{}
			}
		case TokenNumbers:
			tokenValue := string(t.Bytes())

			if pState.IsSingleQuotedString || pState.IsDoubleQuotedString {
				setQuotedString(tokenValue, &command, &pState)
			} else if pState.IsUsing {
				if index.ValidateIndexName(tokenValue) {
					command.Index = tokenValue
					pState.IsUsing = false

					// TokenNumbers is the key of command?
					// using document.db mergeset <TokenNumbers> ...
				}
			} else if pState.IsCommand {
				var (
					keyBytes []byte
					keyType  uint8
				)

				if strings.Contains(tokenValue, ".") {
					tokenFloatValue, err := strconv.ParseFloat(tokenValue, 64)

					if err != nil {
						return fmt.Errorf("Failed to convert %s to float", tokenValue)
					}

					keyBytes = utils.Float64ToBytes(tokenFloatValue)
					keyType = engine.TypeFloat
				} else {
					tokenIntValue, err := strconv.Atoi(tokenValue)

					if err != nil {
						return fmt.Errorf("Failed to convert %s to integer", tokenValue)
					}

					if pState.KVType == engine.TypeUint {
						keyBytes = utils.Uint64ToBytes(uint64(tokenIntValue))
						keyType = engine.TypeUint
					} else if pState.KVType == engine.TypeFloat {
						keyBytes = utils.Float64ToBytes(float64(tokenIntValue))
						keyType = engine.TypeFloat
					} else {
						keyBytes = utils.Int64ToBytes(int64(tokenIntValue))
						keyType = engine.TypeInt
					}
				}

				command.Key = keyBytes
				command.KeyType = keyType
				pState.IsCommand = false
				pState.IsValue = true

				pState.KVType = 0

				// TokenNumbers is the command value?
				// using document.db mergeset name <TokenNumbers>
			} else if pState.IsValue {
				var (
					valueBytes      []byte
					valueType       uint8
					tokenIntValue   int64
					tokenFloatValue float64
					err             error
				)

				if strings.Contains(tokenValue, ".") {
					tokenFloatValue, err = strconv.ParseFloat(tokenValue, 64)

					if err != nil {
						return fmt.Errorf("Failed to convert %s to float", tokenValue)
					}

					valueBytes = utils.Float64ToBytes(tokenFloatValue)
					valueType = engine.TypeFloat
				} else {
					tokenInt, err := strconv.Atoi(tokenValue)

					if err != nil {
						return fmt.Errorf("Failed to convert %s to integer", tokenValue)
					}

					tokenIntValue = int64(tokenInt)
					valueBytes = utils.Int64ToBytes(int64(tokenIntValue))
					valueType = engine.TypeInt
				}

				if command.Command == "mergeset" {
					if valueType == engine.TypeFloat {
						return fmt.Errorf("Failed to parse command. "+
							"MergeSet value shall be a unsigned integer "+
							"value: %v", tokenValue)
					}

					command.Value = utils.Uint64ToBytes(uint64(tokenIntValue))
					command.ValueType = engine.TypeUint
				} else {
					command.Value = valueBytes
					command.ValueType = valueType
				}

				pState.IsValue = false
			}
		default:
			return errors.New("Failed to parse line at '" + string(t.Bytes()) + "'")
		}

		lastTokenType = t.Type()
	}

	// Checks if the last command was correctly parsed but
	// doesn't have the semicolon at the end...
	if validateCommand(command) {
		*listCommands = append(*listCommands, command)
		command = engine.Command{}
		pState = parserState{}

		// Checks if exists a invalid partial command
	} else if command.Index != "" || command.Command != "" ||
		command.Key != nil || command.Value != nil {
		return fmt.Errorf("The last command wasn't correctly finished nor have the semicolon at end: %v", command)
	}

	return nil
}
func TestDateIndex(t *testing.T) {
	var (
		indexName                  = "document-sample-date"
		indexDir                   = DataDirTmp + "/" + indexName
		commands, expectedCommands []engine.Command
		currentDate                = time.Now()
		currentDateStr             = currentDate.Format(time.ANSIC)
		expectedTime, _            = time.Parse(time.ANSIC, currentDateStr)
		expectedNano               = expectedTime.UnixNano()
		docJSON                    = []byte(`{"id": 1, "createAt": "` + currentDateStr + `"}`)
		err                        error
		index                      *Index
		metadata                   = Metadata{
			"id": Metadata{
				"type": "uint",
			},
			"createAt": Metadata{
				"type": "date",
			},
		}
	)

	cfg := Config{
		Debug:   true,
		DataDir: DataDirTmp,
	}

	err = os.MkdirAll(DataDirTmp, 0755)

	if err != nil {
		goto cleanup
	}

	index, err = New(indexName, cfg, true)

	if err != nil {
		t.Error(err)
		goto cleanup
	}

	if _, err := os.Stat(indexDir); os.IsNotExist(err) {
		t.Errorf("no such file or directory: %s", indexDir)
		goto cleanup
	}

	commands, err = index.BuildAdd(1, docJSON, metadata)

	if err != nil {
		t.Error(err.Error())
		goto cleanup
	}

	expectedCommands = []engine.Command{
		{
			Index:     indexName,
			Database:  "document.db",
			Key:       utils.Uint64ToBytes(1),
			KeyType:   engine.TypeUint,
			Value:     docJSON,
			ValueType: engine.TypeString,
			Command:   "set",
		},
		{
			Index:     indexName,
			Database:  "createat_int.idx",
			Key:       utils.Int64ToBytes(expectedNano),
			KeyType:   engine.TypeInt,
			Value:     utils.Uint64ToBytes(1),
			ValueType: engine.TypeUint,
			Command:   "mergeset",
		},
		{
			Index:     indexName,
			Database:  "id_uint.idx",
			Key:       utils.Uint64ToBytes(1),
			KeyType:   engine.TypeUint,
			Value:     utils.Uint64ToBytes(1),
			ValueType: engine.TypeUint,
			Command:   "mergeset",
		},
	}

	if !compareCommands(t, commands, expectedCommands) {
		goto cleanup
	}

cleanup:
	index.Close()
	os.RemoveAll(indexDir)
}
func TestSimpleIndexWithMetadata(t *testing.T) {
	var (
		indexName                  = "document-sample-metadata"
		indexDir                   = DataDirTmp + "/" + indexName
		commands, expectedCommands []engine.Command
		docJSON                    = []byte(`{"id": 1}`)
		err                        error
		index                      *Index
		metadata                   = Metadata{
			"id": Metadata{
				"type": "uint",
			},
		}
	)

	cfg := Config{
		Debug:   false,
		DataDir: DataDirTmp,
	}

	err = os.MkdirAll(DataDirTmp, 0755)

	if err != nil {
		goto cleanup
	}

	index, err = New(indexName, cfg, true)

	if err != nil {
		t.Error(err)
		goto cleanup
	}

	if _, err := os.Stat(indexDir); os.IsNotExist(err) {
		t.Errorf("no such file or directory: %s", indexDir)
		goto cleanup
	}

	commands, err = index.BuildAdd(1, docJSON, metadata)

	if err != nil {
		t.Error(err.Error())
		goto cleanup
	}

	expectedCommands = []engine.Command{
		{
			Index:     indexName,
			Database:  "document.db",
			Key:       utils.Uint64ToBytes(1),
			KeyType:   engine.TypeUint,
			Value:     docJSON,
			ValueType: engine.TypeString,
			Command:   "set",
		},
		{
			Index:     indexName,
			Database:  "id_uint.idx",
			Key:       utils.Uint64ToBytes(1),
			KeyType:   engine.TypeUint,
			Value:     utils.Uint64ToBytes(1),
			ValueType: engine.TypeUint,
			Command:   "mergeset",
		},
	}

	if !compareCommands(t, commands, expectedCommands) {
		goto cleanup
	}

	docJSON = []byte(`{
            "title": "NeoSearch - Reverse Index",
            "description": "Neoway Full Text Search"
        }`)

	metadata = Metadata{
		"title": Metadata{
			"type": "string",
		},
		"description": Metadata{
			"type": "string",
		},
	}

	expectedCommands = []engine.Command{
		{
			Index:     indexName,
			Database:  "document.db",
			Command:   "set",
			Key:       utils.Uint64ToBytes(2),
			KeyType:   engine.TypeUint,
			Value:     docJSON,
			ValueType: engine.TypeString,
		},
		{
			Index:     indexName,
			Database:  "description_string.idx",
			Command:   "mergeset",
			Key:       []byte("neoway"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "description_string.idx",
			Command:   "mergeset",
			Key:       []byte("full"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "description_string.idx",
			Command:   "mergeset",
			Key:       []byte("text"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "description_string.idx",
			Command:   "mergeset",
			Key:       []byte("search"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "description_string.idx",
			Command:   "mergeset",
			Key:       []byte("neoway full text search"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "title_string.idx",
			Command:   "mergeset",
			Key:       []byte("neosearch"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "title_string.idx",
			Command:   "mergeset",
			Key:       []byte("-"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "title_string.idx",
			Command:   "mergeset",
			Key:       []byte("reverse"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "title_string.idx",
			Command:   "mergeset",
			Key:       []byte("index"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
		{
			Index:     indexName,
			Database:  "title_string.idx",
			Command:   "mergeset",
			Key:       []byte("neosearch - reverse index"),
			KeyType:   engine.TypeString,
			Value:     utils.Uint64ToBytes(2),
			ValueType: engine.TypeUint,
		},
	}

	commands, err = index.BuildAdd(2, docJSON, metadata)

	if err != nil {
		t.Error(err)
		goto cleanup
	}

	if !compareCommands(t, commands, expectedCommands) {
		goto cleanup
	}

cleanup:
	index.Close()
	os.RemoveAll(indexDir)
}