Skip to content

Поддержать формат сериализации QueryV2/QueryResultsV2 #141

Description

@MadSchemas

В процессе реализации вложенных join мы пришли к необходимости поменять форматы сериализации Query и QueryResults, так как иначе запаковать древовидную структуру крайне проблематично.

В v5.16.0 общая схема будет такая (для cproto):

  1. Клиент может передавать новый caps о поддержке v2-режима
  2. Сервер может отвечать на этот caps (старый сервер отвечать на него не будет)
  3. По итогу после login сервер и клиент решают, по какой версии протокола они общаются

Для builtin совместимость не сохраняется - байндинг просто передаёт новый капс. Если капс не передан, то библиотека отказывается делать connect.

Предполагается, что в течение некоторого времени и клиент, и сервер будут поддерживать оба варианта, а затем мигрируют на один общий. Основная цель: незаметная миграция при штатных обновлениях

Новый формат для Query:

Query =
    uvarint 2                                    // V2 only; V1 starts at nsName
  · string nsName
  · Item* until tag=End                          // Item tags/payloads same as V1
  · uvarint joinCount · JoinRef{joinCount}       // V2: counted; V1: JoinRef* until EOF (no counts)
  · uvarint mergeCount · JoinRef{mergeCount}     // V2: counted; V1: merges are more JoinRefs in the same EOF stream

JoinRef =
    uvarint joinType
  · Query                                        // V2: full nested Query (own format=2, joins, merges)
                                                 // V1: string nsName · Item* until End (no format=2 / no counts);
                                                 //     further joins spill flat into parent EOF stream
                                                 //     (after Merge, later joins attach to that merge)

Item =
  | Condition:       tag0  · string field · uvarint op · CondValues
  | Distinct:        tag1  · string field                          // legacy read
  | Sort:            tag2  · string expr · uvarint desc · uvarint m · variant{m}
  | JoinOn:          tag3  · uvarint op · uvarint cond · string left · string right
  | Limit:           tag4  · uvarint n
  | Offset:          tag5  · uvarint n
  | ReqTotal:        tag6  · uvarint mode
  | DebugLevel:      tag7  · uvarint level
  | Aggregation:     tag8  · uvarint aggType · uvarint n · string{n}
                     then zero or more of tags {15,16,17} in any order / repeats:
                       tag17 · string expr · uvarint desc
                     | tag15 · uvarint limit
                     | tag16 · uvarint offset
  | SelectFilter:    tag9  · string field
  | SelectFunction:  tag10 · string text
  | End:             tag11                                          // stops Item*
  | Explain:         tag12
  | EqualPosition:   tag13 · uvarint bracketPos · uvarint n · string{n}
  | UpdateField:     tag14 · string col · uvarint n · (uvarint isExpr · variant){n}   // legacy
  | OpenBracket:     tag18 · uvarint op                              // nest; closed by tag19
  | CloseBracket:    tag19
  | JoinCondition:   tag20 · uvarint joinType · uvarint joinIndex   // InnerJoin|OrInnerJoin
  | DropField:       tag21 · string col
  | UpdateObject:    tag22 · string col · uvarint n · uvarint isArray · (uvarint isExpr · variant){n}
  | WithRank:        tag23
  | StrictMode:      tag24 · uvarint mode
  | UpdateFieldV2:   tag25 · string col · uvarint isArray · uvarint n · (uvarint isExpr · variant){n}
  | BetweenFields:   tag26 · uvarint op · string left · uvarint cond · string right
  | AlwaysFalse:     tag27 · uvarint op
  | AlwaysTrue:      tag28 · uvarint op
  | SubQueryCond:    tag29 · uvarint op · string QueryBytes · CondValues
  | FieldSubQuery:   tag30 · uvarint op · string field · uvarint cond · string QueryBytes
  | Local:           tag31
  | Knn:             tag32 · string field · uvarint op · float_vector · KnnParams   // legacy
  | KnnExt:          tag33 · string field · uvarint op · uvarint fmt
                     · (fmt=0 → float_vector | fmt=1 → string) · KnnParams
  | Expressions:     tag36 · Expr · uvarint op · uvarint cond · Expr
  // tag34, tag35 — deprecated, reject

CondValues = uvarint cond · uvarint n · variant{n}   // CondDWithin(11): n=3 as X,Y,distance

KnnParams =
    uvarint type                                // 0=Base, 1=BruteForce, 2=Hnsw, 3=Ivf
  · uvarint version                             // writers emit 1
  · uint8 mask
  · [uvarint k if mask&1] · [float radius if mask&2]
  · [uvarint ef if type=2] · [uvarint nprobe if type=3]

Expr =
    uvarint t
  · t=0 Field:      string name
  | t=1 Values:     uvarint n · variant{n}
  | t=2 Expression: FunctionBlob
  | t=3 SubQuery:   string QueryBytes

QueryBytes = string whose payload is a full Query   // V2 payload includes leading format=2; V1 payload starts at nsName

Новый формат для QueryResults:

QueryResults =
    uvarint flags
  · uvarint totalCount                          // TotalCount()
  · uvarint qcount                              // Count()
  · uvarint fetchCount                          // serialized item count (= fetchLimit)
  · [ PayloadTypes ]?                           // if flags & kResultsWithPayloadTypes
  · Extra*
  · tag End(=0)                                 // ends Extra*
  · Item{fetchCount}

PayloadTypes =
    uvarint updateCount
  · PayloadUpdate{updateCount}

PayloadUpdate =
    uvarint nsid
  · string  nsName
  · uvarint stateToken
  · uvarint tmVersion
  · TagsMatcher                                 // uvarint n · string{n}
  · PayloadType                                 // opaque schema blob (field list)

Extra =
  | Aggregation:        tag1 · slice            // JSON or MsgPack per format mask
  | Explain:            tag2 · slice
  | ShardingVersion:    tag3 · uvarint ver
  | ShardId:            tag4 · uvarint commonShardId
                                                // writer then clears WithShardId for items;
                                                // header flags still show the bit — use Extra shardId
  | IncarnationTags:    tag5 · uvarint shardCount
                        · (varint shardId · uvarint n · varint{n}){shardCount}
  | RankFormat:         tag6 · uvarint format   // must be 0 = SingleFloatValue
                                                // present ⇒ complex rank (float) on items

Item =
    ItemParams
  · [ Joined ]?                                 // if flags & kResultsWithJoined
                                                // (cleared for JSON/MsgPack before write)

ItemParams =
    [ uvarint id · uvarint lsn ]?               // if WithItemID
  · [ uvarint nsid ]?                           // if WithNsID
  · [ Rank ]?                                   // if WithRank
  · [ uint8 raw · (if raw≠0: slice) ]?          // if WithRaw; raw≠0 ⇒ skip shardId + FormatBody
  · [ uvarint shardId ]?                        // if WithShardId and not stopped by raw
                                                // (also absent when common ShardId was in Extra)
  · [ FormatBody ]?                             // omitted when raw≠0

Rank =
    float32                                     // if binding HasComplexRank (often + RankFormat tag6)
  | uvarint                                     // legacy uint16 when no complex-rank capability

FormatBody =
  | format Json|CJson|MsgPack:  slice
  | format Pure:                ε
  | format Ptrs:                u64             // server-local; clients reject

Joined =
    uvarint fieldsCount                         // 0 ⇒ no join fields follow
  · (if fieldsCount > 0) Field{fieldsCount}

Field =
    uvarint itemsCount
  · JoinedItem{itemsCount}                      // empty if itemsCount=0

JoinedItem =
    Item                                        // V2: full Item = ItemParams · [Joined]  (recursive)
  | ItemParams                                  // V1: ItemParams only (one join level, no nested Joined)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions