Implement UpdateDataset and UpdateMetadata in GO (#13928)

### What problem does this PR solve?

Implement UpdateDataset and UpdateMetadata in GO

Add cli:
UPDATE CHUNK <chunk_id> OF DATASET <dataset_name> SET <update_fields>
REMOVE TAGS 'tag1', 'tag2' from DATASET 'dataset_name';
SET METADATA OF DOCUMENT <doc_id> TO <meta>


### Type of change

- [ ] Refactoring
This commit is contained in:
qinling0210
2026-04-07 09:44:51 +08:00
committed by GitHub
parent 60ec5880e5
commit 49386bc1b5
27 changed files with 1620 additions and 171 deletions

View File

@@ -33,12 +33,8 @@ func (p *Parser) parseLoginUser() (*Command, error) {
cmd.Params["email"] = email
p.nextToken()
// Optional: WITH PASSWORD 'password'
if p.curToken.Type == TokenWith {
p.nextToken()
if p.curToken.Type != TokenPassword {
return nil, fmt.Errorf("expected PASSWORD after WITH")
}
// Optional: PASSWORD 'password'
if p.curToken.Type == TokenPassword {
p.nextToken()
password, err := p.parseQuotedString()
if err != nil {
@@ -853,6 +849,17 @@ func (p *Parser) parseDeleteCommand() (*Command, error) {
}
}
func (p *Parser) parseRemoveCommand() (*Command, error) {
p.nextToken() // consume RM
switch p.curToken.Type {
case TokenTag:
return p.parseRemoveTags()
default:
return nil, fmt.Errorf("unknown REMOVE target: %s", p.curToken.Value)
}
}
func (p *Parser) parseDropToken() (*Command, error) {
p.nextToken() // consume TOKEN
@@ -1574,6 +1581,9 @@ func (p *Parser) parseSetCommand() (*Command, error) {
if p.curToken.Type == TokenToken {
return p.parseSetToken()
}
if p.curToken.Type == TokenMetadata {
return p.parseSetMeta()
}
return nil, fmt.Errorf("unknown SET target: %s", p.curToken.Value)
}
@@ -2229,7 +2239,10 @@ func (p *Parser) parseUserStatement() (*Command, error) {
return p.parseInsertCommand()
case TokenSearch:
return p.parseSearchCommand()
case TokenUpdate:
return p.parseUpdateCommand()
case TokenRemove:
return p.parseRemoveCommand()
default:
return nil, fmt.Errorf("invalid user statement: %s", p.curToken.Value)
}
@@ -2318,3 +2331,164 @@ func (p *Parser) parseUnsetCommand() (*Command, error) {
}
return NewCommand("unset_token"), nil
}
// parseUpdateCommand parses UPDATE CHUNK command
// UPDATE CHUNK 'chunk_id' OF DATASET 'dataset_name' SET '{"content": "..."}'
func (p *Parser) parseUpdateCommand() (*Command, error) {
p.nextToken() // consume UPDATE
if p.curToken.Type != TokenChunk {
return nil, fmt.Errorf("expected CHUNK after UPDATE")
}
p.nextToken()
// Parse chunk_id
chunkID, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected chunk_id: %w", err)
}
cmd := NewCommand("update_chunk")
cmd.Params["chunk_id"] = chunkID
p.nextToken()
if p.curToken.Type != TokenOf {
return nil, fmt.Errorf("expected OF after chunk_id")
}
p.nextToken()
if p.curToken.Type != TokenDataset {
return nil, fmt.Errorf("expected DATASET after OF")
}
p.nextToken()
// Parse dataset_name
datasetName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected dataset_name: %w", err)
}
cmd.Params["dataset_name"] = datasetName
p.nextToken()
if p.curToken.Type != TokenSet {
return nil, fmt.Errorf("expected SET after dataset_name")
}
p.nextToken()
// Parse JSON body
jsonBody, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected JSON body: %w", err)
}
cmd.Params["json_body"] = jsonBody
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// parseSetMeta parses: SET METADATA OF DOCUMENT 'doc_id' TO '{"key": "value"}'
func (p *Parser) parseSetMeta() (*Command, error) {
p.nextToken() // consume METADATA
// Expect OF
if p.curToken.Type != TokenOf {
return nil, fmt.Errorf("expected OF after SET METADATA")
}
p.nextToken()
// Expect DOCUMENT
if p.curToken.Type != TokenDocument {
return nil, fmt.Errorf("expected DOCUMENT after SET METADATA OF")
}
p.nextToken()
// Parse doc_id
docID, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected doc_id: %w", err)
}
cmd := NewCommand("set_meta")
cmd.Params["doc_id"] = docID
p.nextToken()
// Expect TO
if p.curToken.Type != TokenTo {
return nil, fmt.Errorf("expected TO after doc_id")
}
p.nextToken()
// Parse meta JSON
meta, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected meta JSON: %w", err)
}
cmd.Params["meta"] = meta
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// parseRemoveTags parses: REMOVE TAGS 'tag1', 'tag2' from DATASET 'dataset_name';
func (p *Parser) parseRemoveTags() (*Command, error) {
p.nextToken() // consume TAGS
// Parse first tag
tag, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected tag: %w", err)
}
tags := []string{tag}
// Parse additional tags separated by commas
for {
p.nextToken()
if p.curToken.Type == TokenComma {
p.nextToken()
tag, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected tag after comma: %w", err)
}
tags = append(tags, tag)
} else {
break
}
}
cmd := NewCommand("rm_tags")
cmd.Params["tags"] = tags
// Expect from
if p.curToken.Type != TokenFrom {
return nil, fmt.Errorf("expected FROM after tags")
}
p.nextToken()
// Expect DATASET
if p.curToken.Type != TokenDataset {
return nil, fmt.Errorf("expected DATASET after FROM")
}
p.nextToken()
// Parse dataset_name
datasetName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected dataset_name: %w", err)
}
cmd.Params["dataset_name"] = datasetName
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}