diff --git a/drip/api.gen.go b/drip/api.gen.go index e615be0..a617eec 100644 --- a/drip/api.gen.go +++ b/drip/api.gen.go @@ -284,8 +284,7 @@ type NodeStatus string // NodeVersion defines model for NodeVersion. type NodeVersion struct { // Changelog Summary of changes made in this version - Changelog *string `json:"changelog,omitempty"` - ComfyNodes *map[string]ComfyNode `json:"comfy_nodes,omitempty"` + Changelog *string `json:"changelog,omitempty"` // CreatedAt The date and time the version was created. CreatedAt *time.Time `json:"createdAt,omitempty"` @@ -490,6 +489,15 @@ type ListAllNodesParams struct { IncludeBanned *bool `form:"include_banned,omitempty" json:"include_banned,omitempty"` } +// ReindexNodesParams defines parameters for ReindexNodes. +type ReindexNodesParams struct { + // MaxBatch Maximum number of nodes to send to algolia at a time + MaxBatch *int `form:"max_batch,omitempty" json:"max_batch,omitempty"` + + // MinAge Minimum interval from the last time the nodes were indexed to algolia + MinAge *time.Duration `form:"min_age,omitempty" json:"min_age,omitempty"` +} + // SearchNodesParams defines parameters for SearchNodes. type SearchNodesParams struct { // Page Page number of the nodes list @@ -691,7 +699,7 @@ type ServerInterface interface { ListAllNodes(ctx echo.Context, params ListAllNodesParams) error // Reindex all nodes for searching. // (POST /nodes/reindex) - ReindexNodes(ctx echo.Context) error + ReindexNodes(ctx echo.Context, params ReindexNodesParams) error // Retrieves a list of nodes // (GET /nodes/search) SearchNodes(ctx echo.Context, params SearchNodesParams) error @@ -998,8 +1006,24 @@ func (w *ServerInterfaceWrapper) ListAllNodes(ctx echo.Context) error { func (w *ServerInterfaceWrapper) ReindexNodes(ctx echo.Context) error { var err error + // Parameter object where we will unmarshal all parameters from the context + var params ReindexNodesParams + // ------------- Optional query parameter "max_batch" ------------- + + err = runtime.BindQueryParameter("form", true, false, "max_batch", ctx.QueryParams(), ¶ms.MaxBatch) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter max_batch: %s", err)) + } + + // ------------- Optional query parameter "min_age" ------------- + + err = runtime.BindQueryParameter("form", true, false, "min_age", ctx.QueryParams(), ¶ms.MinAge) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter min_age: %s", err)) + } + // Invoke the callback with all the unmarshaled arguments - err = w.Handler.ReindexNodes(ctx) + err = w.Handler.ReindexNodes(ctx, params) return err } @@ -2103,6 +2127,7 @@ func (response ListAllNodes500JSONResponse) VisitListAllNodesResponse(w http.Res } type ReindexNodesRequestObject struct { + Params ReindexNodesParams } type ReindexNodesResponseObject interface { @@ -3977,9 +4002,11 @@ func (sh *strictHandler) ListAllNodes(ctx echo.Context, params ListAllNodesParam } // ReindexNodes operation middleware -func (sh *strictHandler) ReindexNodes(ctx echo.Context) error { +func (sh *strictHandler) ReindexNodes(ctx echo.Context, params ReindexNodesParams) error { var request ReindexNodesRequestObject + request.Params = params + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { return sh.ssi.ReindexNodes(ctx.Request().Context(), request.(ReindexNodesRequestObject)) } @@ -4942,84 +4969,85 @@ var swaggerSpec = []string{ "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/ent/migrate/migrations/20250123210944_migration.sql b/ent/migrate/migrations/20250123210944_migration.sql new file mode 100644 index 0000000..5719403 --- /dev/null +++ b/ent/migrate/migrations/20250123210944_migration.sql @@ -0,0 +1,2 @@ +-- Modify "nodes" table +ALTER TABLE "nodes" ADD COLUMN "last_algolia_index_time" timestamptz NULL; diff --git a/ent/migrate/migrations/atlas.sum b/ent/migrate/migrations/atlas.sum index 6dd48e5..7609d59 100644 --- a/ent/migrate/migrations/atlas.sum +++ b/ent/migrate/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:kqIP+0gVgOv7M2GW0XGPm0MAeU3BwfpfoeLNIu9Rxaw= +h1:tvIMkA0ld8Lhc+57OLo5mxHU+3NkkmhSEHVPfKJ8RhM= 20240526144817_migration.sql h1:sP6keX+oMyLL2qpIFx0Ns0WYfWM5hJ4zkFPmLWT68fM= 20240528220411_migration.sql h1:SR44sOEaWbDgYCKJZIKcGCI7Ta+LqL71z225Nhs2+HM= 20240528221846_migration.sql h1:EkUonGI9Bu689qWX4pG3PRC+On4f6u7UvwDbaR8mCNk= @@ -23,3 +23,4 @@ h1:kqIP+0gVgOv7M2GW0XGPm0MAeU3BwfpfoeLNIu9Rxaw= 20250111185452_migration.sql h1:fboQ7cjU7YbEwzUek3YGtWt7gdZK+HUrjqQOpKbDPD4= 20250111220145_migration.sql h1:r5D0DeFYV4llyfBcD2BPzHacDWGWfVNe0nVrKwfWgJE= 20250113223458_migration.sql h1:/y9TETVsoKY2x0+rhT1DjTVJV/LghpK/RW4RMEhoVY0= +20250123210944_migration.sql h1:rgKGRZPgTXXBX7RUxEKDpk3X8MbDqJiU5981dg7bbbU= diff --git a/ent/migrate/schema.go b/ent/migrate/schema.go index f2e51cf..1afa080 100644 --- a/ent/migrate/schema.go +++ b/ent/migrate/schema.go @@ -120,6 +120,7 @@ var ( {Name: "total_review", Type: field.TypeInt64, Default: 0}, {Name: "status", Type: field.TypeEnum, Enums: []string{"active", "banned", "deleted"}, Default: "active"}, {Name: "status_detail", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}}, + {Name: "last_algolia_index_time", Type: field.TypeTime, Nullable: true}, {Name: "publisher_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}}, } // NodesTable holds the schema information for the "nodes" table. @@ -130,7 +131,7 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "nodes_publishers_nodes", - Columns: []*schema.Column{NodesColumns[16]}, + Columns: []*schema.Column{NodesColumns[17]}, RefColumns: []*schema.Column{PublishersColumns[0]}, OnDelete: schema.NoAction, }, diff --git a/ent/mutation.go b/ent/mutation.go index b71dedf..fc34f20 100644 --- a/ent/mutation.go +++ b/ent/mutation.go @@ -3998,40 +3998,41 @@ func (m *GitCommitMutation) ResetEdge(name string) error { // NodeMutation represents an operation that mutates the Node nodes in the graph. type NodeMutation struct { config - op Op - typ string - id *string - create_time *time.Time - update_time *time.Time - name *string - description *string - category *string - author *string - license *string - repository_url *string - icon_url *string - tags *[]string - appendtags []string - total_install *int64 - addtotal_install *int64 - total_star *int64 - addtotal_star *int64 - total_review *int64 - addtotal_review *int64 - status *schema.NodeStatus - status_detail *string - clearedFields map[string]struct{} - publisher *string - clearedpublisher bool - versions map[uuid.UUID]struct{} - removedversions map[uuid.UUID]struct{} - clearedversions bool - reviews map[uuid.UUID]struct{} - removedreviews map[uuid.UUID]struct{} - clearedreviews bool - done bool - oldValue func(context.Context) (*Node, error) - predicates []predicate.Node + op Op + typ string + id *string + create_time *time.Time + update_time *time.Time + name *string + description *string + category *string + author *string + license *string + repository_url *string + icon_url *string + tags *[]string + appendtags []string + total_install *int64 + addtotal_install *int64 + total_star *int64 + addtotal_star *int64 + total_review *int64 + addtotal_review *int64 + status *schema.NodeStatus + status_detail *string + last_algolia_index_time *time.Time + clearedFields map[string]struct{} + publisher *string + clearedpublisher bool + versions map[uuid.UUID]struct{} + removedversions map[uuid.UUID]struct{} + clearedversions bool + reviews map[uuid.UUID]struct{} + removedreviews map[uuid.UUID]struct{} + clearedreviews bool + done bool + oldValue func(context.Context) (*Node, error) + predicates []predicate.Node } var _ ent.Mutation = (*NodeMutation)(nil) @@ -4854,6 +4855,55 @@ func (m *NodeMutation) ResetStatusDetail() { delete(m.clearedFields, node.FieldStatusDetail) } +// SetLastAlgoliaIndexTime sets the "last_algolia_index_time" field. +func (m *NodeMutation) SetLastAlgoliaIndexTime(t time.Time) { + m.last_algolia_index_time = &t +} + +// LastAlgoliaIndexTime returns the value of the "last_algolia_index_time" field in the mutation. +func (m *NodeMutation) LastAlgoliaIndexTime() (r time.Time, exists bool) { + v := m.last_algolia_index_time + if v == nil { + return + } + return *v, true +} + +// OldLastAlgoliaIndexTime returns the old "last_algolia_index_time" field's value of the Node entity. +// If the Node object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NodeMutation) OldLastAlgoliaIndexTime(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLastAlgoliaIndexTime is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLastAlgoliaIndexTime requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLastAlgoliaIndexTime: %w", err) + } + return oldValue.LastAlgoliaIndexTime, nil +} + +// ClearLastAlgoliaIndexTime clears the value of the "last_algolia_index_time" field. +func (m *NodeMutation) ClearLastAlgoliaIndexTime() { + m.last_algolia_index_time = nil + m.clearedFields[node.FieldLastAlgoliaIndexTime] = struct{}{} +} + +// LastAlgoliaIndexTimeCleared returns if the "last_algolia_index_time" field was cleared in this mutation. +func (m *NodeMutation) LastAlgoliaIndexTimeCleared() bool { + _, ok := m.clearedFields[node.FieldLastAlgoliaIndexTime] + return ok +} + +// ResetLastAlgoliaIndexTime resets all changes to the "last_algolia_index_time" field. +func (m *NodeMutation) ResetLastAlgoliaIndexTime() { + m.last_algolia_index_time = nil + delete(m.clearedFields, node.FieldLastAlgoliaIndexTime) +} + // ClearPublisher clears the "publisher" edge to the Publisher entity. func (m *NodeMutation) ClearPublisher() { m.clearedpublisher = true @@ -5023,7 +5073,7 @@ func (m *NodeMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *NodeMutation) Fields() []string { - fields := make([]string, 0, 16) + fields := make([]string, 0, 17) if m.create_time != nil { fields = append(fields, node.FieldCreateTime) } @@ -5072,6 +5122,9 @@ func (m *NodeMutation) Fields() []string { if m.status_detail != nil { fields = append(fields, node.FieldStatusDetail) } + if m.last_algolia_index_time != nil { + fields = append(fields, node.FieldLastAlgoliaIndexTime) + } return fields } @@ -5112,6 +5165,8 @@ func (m *NodeMutation) Field(name string) (ent.Value, bool) { return m.Status() case node.FieldStatusDetail: return m.StatusDetail() + case node.FieldLastAlgoliaIndexTime: + return m.LastAlgoliaIndexTime() } return nil, false } @@ -5153,6 +5208,8 @@ func (m *NodeMutation) OldField(ctx context.Context, name string) (ent.Value, er return m.OldStatus(ctx) case node.FieldStatusDetail: return m.OldStatusDetail(ctx) + case node.FieldLastAlgoliaIndexTime: + return m.OldLastAlgoliaIndexTime(ctx) } return nil, fmt.Errorf("unknown Node field %s", name) } @@ -5274,6 +5331,13 @@ func (m *NodeMutation) SetField(name string, value ent.Value) error { } m.SetStatusDetail(v) return nil + case node.FieldLastAlgoliaIndexTime: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLastAlgoliaIndexTime(v) + return nil } return fmt.Errorf("unknown Node field %s", name) } @@ -5358,6 +5422,9 @@ func (m *NodeMutation) ClearedFields() []string { if m.FieldCleared(node.FieldStatusDetail) { fields = append(fields, node.FieldStatusDetail) } + if m.FieldCleared(node.FieldLastAlgoliaIndexTime) { + fields = append(fields, node.FieldLastAlgoliaIndexTime) + } return fields } @@ -5387,6 +5454,9 @@ func (m *NodeMutation) ClearField(name string) error { case node.FieldStatusDetail: m.ClearStatusDetail() return nil + case node.FieldLastAlgoliaIndexTime: + m.ClearLastAlgoliaIndexTime() + return nil } return fmt.Errorf("unknown Node nullable field %s", name) } @@ -5443,6 +5513,9 @@ func (m *NodeMutation) ResetField(name string) error { case node.FieldStatusDetail: m.ResetStatusDetail() return nil + case node.FieldLastAlgoliaIndexTime: + m.ResetLastAlgoliaIndexTime() + return nil } return fmt.Errorf("unknown Node field %s", name) } diff --git a/ent/node.go b/ent/node.go index 7fd2634..7774cd6 100644 --- a/ent/node.go +++ b/ent/node.go @@ -52,6 +52,8 @@ type Node struct { Status schema.NodeStatus `json:"status,omitempty"` // StatusDetail holds the value of the "status_detail" field. StatusDetail string `json:"status_detail,omitempty"` + // LastAlgoliaIndexTime holds the value of the "last_algolia_index_time" field. + LastAlgoliaIndexTime time.Time `json:"last_algolia_index_time,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the NodeQuery when eager-loading is set. Edges NodeEdges `json:"edges"` @@ -111,7 +113,7 @@ func (*Node) scanValues(columns []string) ([]any, error) { values[i] = new(sql.NullInt64) case node.FieldID, node.FieldPublisherID, node.FieldName, node.FieldDescription, node.FieldCategory, node.FieldAuthor, node.FieldLicense, node.FieldRepositoryURL, node.FieldIconURL, node.FieldStatus, node.FieldStatusDetail: values[i] = new(sql.NullString) - case node.FieldCreateTime, node.FieldUpdateTime: + case node.FieldCreateTime, node.FieldUpdateTime, node.FieldLastAlgoliaIndexTime: values[i] = new(sql.NullTime) default: values[i] = new(sql.UnknownType) @@ -232,6 +234,12 @@ func (n *Node) assignValues(columns []string, values []any) error { } else if value.Valid { n.StatusDetail = value.String } + case node.FieldLastAlgoliaIndexTime: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field last_algolia_index_time", values[i]) + } else if value.Valid { + n.LastAlgoliaIndexTime = value.Time + } default: n.selectValues.Set(columns[i], values[i]) } @@ -330,6 +338,9 @@ func (n *Node) String() string { builder.WriteString(", ") builder.WriteString("status_detail=") builder.WriteString(n.StatusDetail) + builder.WriteString(", ") + builder.WriteString("last_algolia_index_time=") + builder.WriteString(n.LastAlgoliaIndexTime.Format(time.ANSIC)) builder.WriteByte(')') return builder.String() } diff --git a/ent/node/node.go b/ent/node/node.go index b835a98..d170e07 100644 --- a/ent/node/node.go +++ b/ent/node/node.go @@ -48,6 +48,8 @@ const ( FieldStatus = "status" // FieldStatusDetail holds the string denoting the status_detail field in the database. FieldStatusDetail = "status_detail" + // FieldLastAlgoliaIndexTime holds the string denoting the last_algolia_index_time field in the database. + FieldLastAlgoliaIndexTime = "last_algolia_index_time" // EdgePublisher holds the string denoting the publisher edge name in mutations. EdgePublisher = "publisher" // EdgeVersions holds the string denoting the versions edge name in mutations. @@ -98,6 +100,7 @@ var Columns = []string{ FieldTotalReview, FieldStatus, FieldStatusDetail, + FieldLastAlgoliaIndexTime, } // ValidColumn reports if the column name is valid (part of the table columns). @@ -222,6 +225,11 @@ func ByStatusDetail(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldStatusDetail, opts...).ToFunc() } +// ByLastAlgoliaIndexTime orders the results by the last_algolia_index_time field. +func ByLastAlgoliaIndexTime(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastAlgoliaIndexTime, opts...).ToFunc() +} + // ByPublisherField orders the results by publisher field. func ByPublisherField(field string, opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { diff --git a/ent/node/where.go b/ent/node/where.go index 3531dc0..9cdcd66 100644 --- a/ent/node/where.go +++ b/ent/node/where.go @@ -136,6 +136,11 @@ func StatusDetail(v string) predicate.Node { return predicate.Node(sql.FieldEQ(FieldStatusDetail, v)) } +// LastAlgoliaIndexTime applies equality check predicate on the "last_algolia_index_time" field. It's identical to LastAlgoliaIndexTimeEQ. +func LastAlgoliaIndexTime(v time.Time) predicate.Node { + return predicate.Node(sql.FieldEQ(FieldLastAlgoliaIndexTime, v)) +} + // CreateTimeEQ applies the EQ predicate on the "create_time" field. func CreateTimeEQ(v time.Time) predicate.Node { return predicate.Node(sql.FieldEQ(FieldCreateTime, v)) @@ -1001,6 +1006,56 @@ func StatusDetailContainsFold(v string) predicate.Node { return predicate.Node(sql.FieldContainsFold(FieldStatusDetail, v)) } +// LastAlgoliaIndexTimeEQ applies the EQ predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeEQ(v time.Time) predicate.Node { + return predicate.Node(sql.FieldEQ(FieldLastAlgoliaIndexTime, v)) +} + +// LastAlgoliaIndexTimeNEQ applies the NEQ predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeNEQ(v time.Time) predicate.Node { + return predicate.Node(sql.FieldNEQ(FieldLastAlgoliaIndexTime, v)) +} + +// LastAlgoliaIndexTimeIn applies the In predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeIn(vs ...time.Time) predicate.Node { + return predicate.Node(sql.FieldIn(FieldLastAlgoliaIndexTime, vs...)) +} + +// LastAlgoliaIndexTimeNotIn applies the NotIn predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeNotIn(vs ...time.Time) predicate.Node { + return predicate.Node(sql.FieldNotIn(FieldLastAlgoliaIndexTime, vs...)) +} + +// LastAlgoliaIndexTimeGT applies the GT predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeGT(v time.Time) predicate.Node { + return predicate.Node(sql.FieldGT(FieldLastAlgoliaIndexTime, v)) +} + +// LastAlgoliaIndexTimeGTE applies the GTE predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeGTE(v time.Time) predicate.Node { + return predicate.Node(sql.FieldGTE(FieldLastAlgoliaIndexTime, v)) +} + +// LastAlgoliaIndexTimeLT applies the LT predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeLT(v time.Time) predicate.Node { + return predicate.Node(sql.FieldLT(FieldLastAlgoliaIndexTime, v)) +} + +// LastAlgoliaIndexTimeLTE applies the LTE predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeLTE(v time.Time) predicate.Node { + return predicate.Node(sql.FieldLTE(FieldLastAlgoliaIndexTime, v)) +} + +// LastAlgoliaIndexTimeIsNil applies the IsNil predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeIsNil() predicate.Node { + return predicate.Node(sql.FieldIsNull(FieldLastAlgoliaIndexTime)) +} + +// LastAlgoliaIndexTimeNotNil applies the NotNil predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeNotNil() predicate.Node { + return predicate.Node(sql.FieldNotNull(FieldLastAlgoliaIndexTime)) +} + // HasPublisher applies the HasEdge predicate on the "publisher" edge. func HasPublisher() predicate.Node { return predicate.Node(func(s *sql.Selector) { diff --git a/ent/node_create.go b/ent/node_create.go index 749c76e..9ecbcc7 100644 --- a/ent/node_create.go +++ b/ent/node_create.go @@ -212,6 +212,20 @@ func (nc *NodeCreate) SetNillableStatusDetail(s *string) *NodeCreate { return nc } +// SetLastAlgoliaIndexTime sets the "last_algolia_index_time" field. +func (nc *NodeCreate) SetLastAlgoliaIndexTime(t time.Time) *NodeCreate { + nc.mutation.SetLastAlgoliaIndexTime(t) + return nc +} + +// SetNillableLastAlgoliaIndexTime sets the "last_algolia_index_time" field if the given value is not nil. +func (nc *NodeCreate) SetNillableLastAlgoliaIndexTime(t *time.Time) *NodeCreate { + if t != nil { + nc.SetLastAlgoliaIndexTime(*t) + } + return nc +} + // SetID sets the "id" field. func (nc *NodeCreate) SetID(s string) *NodeCreate { nc.mutation.SetID(s) @@ -457,6 +471,10 @@ func (nc *NodeCreate) createSpec() (*Node, *sqlgraph.CreateSpec) { _spec.SetField(node.FieldStatusDetail, field.TypeString, value) _node.StatusDetail = value } + if value, ok := nc.mutation.LastAlgoliaIndexTime(); ok { + _spec.SetField(node.FieldLastAlgoliaIndexTime, field.TypeTime, value) + _node.LastAlgoliaIndexTime = value + } if nodes := nc.mutation.PublisherIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2O, @@ -786,6 +804,24 @@ func (u *NodeUpsert) ClearStatusDetail() *NodeUpsert { return u } +// SetLastAlgoliaIndexTime sets the "last_algolia_index_time" field. +func (u *NodeUpsert) SetLastAlgoliaIndexTime(v time.Time) *NodeUpsert { + u.Set(node.FieldLastAlgoliaIndexTime, v) + return u +} + +// UpdateLastAlgoliaIndexTime sets the "last_algolia_index_time" field to the value that was provided on create. +func (u *NodeUpsert) UpdateLastAlgoliaIndexTime() *NodeUpsert { + u.SetExcluded(node.FieldLastAlgoliaIndexTime) + return u +} + +// ClearLastAlgoliaIndexTime clears the value of the "last_algolia_index_time" field. +func (u *NodeUpsert) ClearLastAlgoliaIndexTime() *NodeUpsert { + u.SetNull(node.FieldLastAlgoliaIndexTime) + return u +} + // UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // @@ -1103,6 +1139,27 @@ func (u *NodeUpsertOne) ClearStatusDetail() *NodeUpsertOne { }) } +// SetLastAlgoliaIndexTime sets the "last_algolia_index_time" field. +func (u *NodeUpsertOne) SetLastAlgoliaIndexTime(v time.Time) *NodeUpsertOne { + return u.Update(func(s *NodeUpsert) { + s.SetLastAlgoliaIndexTime(v) + }) +} + +// UpdateLastAlgoliaIndexTime sets the "last_algolia_index_time" field to the value that was provided on create. +func (u *NodeUpsertOne) UpdateLastAlgoliaIndexTime() *NodeUpsertOne { + return u.Update(func(s *NodeUpsert) { + s.UpdateLastAlgoliaIndexTime() + }) +} + +// ClearLastAlgoliaIndexTime clears the value of the "last_algolia_index_time" field. +func (u *NodeUpsertOne) ClearLastAlgoliaIndexTime() *NodeUpsertOne { + return u.Update(func(s *NodeUpsert) { + s.ClearLastAlgoliaIndexTime() + }) +} + // Exec executes the query. func (u *NodeUpsertOne) Exec(ctx context.Context) error { if len(u.create.conflict) == 0 { @@ -1587,6 +1644,27 @@ func (u *NodeUpsertBulk) ClearStatusDetail() *NodeUpsertBulk { }) } +// SetLastAlgoliaIndexTime sets the "last_algolia_index_time" field. +func (u *NodeUpsertBulk) SetLastAlgoliaIndexTime(v time.Time) *NodeUpsertBulk { + return u.Update(func(s *NodeUpsert) { + s.SetLastAlgoliaIndexTime(v) + }) +} + +// UpdateLastAlgoliaIndexTime sets the "last_algolia_index_time" field to the value that was provided on create. +func (u *NodeUpsertBulk) UpdateLastAlgoliaIndexTime() *NodeUpsertBulk { + return u.Update(func(s *NodeUpsert) { + s.UpdateLastAlgoliaIndexTime() + }) +} + +// ClearLastAlgoliaIndexTime clears the value of the "last_algolia_index_time" field. +func (u *NodeUpsertBulk) ClearLastAlgoliaIndexTime() *NodeUpsertBulk { + return u.Update(func(s *NodeUpsert) { + s.ClearLastAlgoliaIndexTime() + }) +} + // Exec executes the query. func (u *NodeUpsertBulk) Exec(ctx context.Context) error { if u.create.err != nil { diff --git a/ent/node_update.go b/ent/node_update.go index d84f7a2..3145c7b 100644 --- a/ent/node_update.go +++ b/ent/node_update.go @@ -286,6 +286,26 @@ func (nu *NodeUpdate) ClearStatusDetail() *NodeUpdate { return nu } +// SetLastAlgoliaIndexTime sets the "last_algolia_index_time" field. +func (nu *NodeUpdate) SetLastAlgoliaIndexTime(t time.Time) *NodeUpdate { + nu.mutation.SetLastAlgoliaIndexTime(t) + return nu +} + +// SetNillableLastAlgoliaIndexTime sets the "last_algolia_index_time" field if the given value is not nil. +func (nu *NodeUpdate) SetNillableLastAlgoliaIndexTime(t *time.Time) *NodeUpdate { + if t != nil { + nu.SetLastAlgoliaIndexTime(*t) + } + return nu +} + +// ClearLastAlgoliaIndexTime clears the value of the "last_algolia_index_time" field. +func (nu *NodeUpdate) ClearLastAlgoliaIndexTime() *NodeUpdate { + nu.mutation.ClearLastAlgoliaIndexTime() + return nu +} + // SetPublisher sets the "publisher" edge to the Publisher entity. func (nu *NodeUpdate) SetPublisher(p *Publisher) *NodeUpdate { return nu.SetPublisherID(p.ID) @@ -512,6 +532,12 @@ func (nu *NodeUpdate) sqlSave(ctx context.Context) (n int, err error) { if nu.mutation.StatusDetailCleared() { _spec.ClearField(node.FieldStatusDetail, field.TypeString) } + if value, ok := nu.mutation.LastAlgoliaIndexTime(); ok { + _spec.SetField(node.FieldLastAlgoliaIndexTime, field.TypeTime, value) + } + if nu.mutation.LastAlgoliaIndexTimeCleared() { + _spec.ClearField(node.FieldLastAlgoliaIndexTime, field.TypeTime) + } if nu.mutation.PublisherCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2O, @@ -904,6 +930,26 @@ func (nuo *NodeUpdateOne) ClearStatusDetail() *NodeUpdateOne { return nuo } +// SetLastAlgoliaIndexTime sets the "last_algolia_index_time" field. +func (nuo *NodeUpdateOne) SetLastAlgoliaIndexTime(t time.Time) *NodeUpdateOne { + nuo.mutation.SetLastAlgoliaIndexTime(t) + return nuo +} + +// SetNillableLastAlgoliaIndexTime sets the "last_algolia_index_time" field if the given value is not nil. +func (nuo *NodeUpdateOne) SetNillableLastAlgoliaIndexTime(t *time.Time) *NodeUpdateOne { + if t != nil { + nuo.SetLastAlgoliaIndexTime(*t) + } + return nuo +} + +// ClearLastAlgoliaIndexTime clears the value of the "last_algolia_index_time" field. +func (nuo *NodeUpdateOne) ClearLastAlgoliaIndexTime() *NodeUpdateOne { + nuo.mutation.ClearLastAlgoliaIndexTime() + return nuo +} + // SetPublisher sets the "publisher" edge to the Publisher entity. func (nuo *NodeUpdateOne) SetPublisher(p *Publisher) *NodeUpdateOne { return nuo.SetPublisherID(p.ID) @@ -1160,6 +1206,12 @@ func (nuo *NodeUpdateOne) sqlSave(ctx context.Context) (_node *Node, err error) if nuo.mutation.StatusDetailCleared() { _spec.ClearField(node.FieldStatusDetail, field.TypeString) } + if value, ok := nuo.mutation.LastAlgoliaIndexTime(); ok { + _spec.SetField(node.FieldLastAlgoliaIndexTime, field.TypeTime, value) + } + if nuo.mutation.LastAlgoliaIndexTimeCleared() { + _spec.ClearField(node.FieldLastAlgoliaIndexTime, field.TypeTime) + } if nuo.mutation.PublisherCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2O, diff --git a/ent/schema/node.go b/ent/schema/node.go index eff4878..7e35cad 100644 --- a/ent/schema/node.go +++ b/ent/schema/node.go @@ -55,6 +55,7 @@ func (Node) Fields() []ent.Field { field.String("status_detail").SchemaType(map[string]string{ dialect.Postgres: "text", }).Optional(), + field.Time("last_algolia_index_time").Optional(), } } diff --git a/integration-tests/comfy_node_test.go b/integration-tests/comfy_node_test.go index eaa16b4..90fc326 100644 --- a/integration-tests/comfy_node_test.go +++ b/integration-tests/comfy_node_test.go @@ -99,13 +99,12 @@ func TestRegistryComfyNode(t *testing.T) { // Test case: Ensure no comfy nodes exist initially t.Run("NoComfyNode", func(t *testing.T) { - res, err := withMiddleware(authz, impl.GetNodeVersion)(ctx, drip.GetNodeVersionRequestObject{ - NodeId: *node.Id, - VersionId: *nodeVersionExtractionSucceeded1.Version, + res, err := withMiddleware(authz, impl.GetComfyNode)(ctx, drip.GetComfyNodeRequestObject{ + NodeId: *node.Id, + Version: *nodeVersionExtractionSucceeded1.Version, }) require.NoError(t, err, "should not return error") - require.IsType(t, drip.GetNodeVersion200JSONResponse{}, res) - assert.Empty(t, res.(drip.GetNodeVersion200JSONResponse).ComfyNodes) + require.IsType(t, drip.GetComfyNode404JSONResponse{}, res) }) t.Run("CreateComfyNodes", func(t *testing.T) { @@ -170,46 +169,6 @@ func TestRegistryComfyNode(t *testing.T) { require.IsType(t, drip.CreateComfyNodes409JSONResponse{}, res) }) - // Test case: Retrieve node version - t.Run("GetNodeVersion contain comfy nodes", func(t *testing.T) { - res, err := withMiddleware(authz, impl.GetNodeVersion)(ctx, drip.GetNodeVersionRequestObject{ - NodeId: *node.Id, - VersionId: *nodeVersionExtractionSucceeded1.Version, - }) - require.NoError(t, err, "should return created node version") - require.IsType(t, drip.GetNodeVersion200JSONResponse{}, res) - for k, v := range *res.(drip.GetNodeVersion200JSONResponse).ComfyNodes { - ev := (*comfyNodes.Nodes)[k] - ev.ComfyNodeId = proto.String(k) - assert.Equal(t, ev, v) - } - }) - - // Test case: List node versions - t.Run("ListNodeVersion contains comfy nodes", func(t *testing.T) { - res, err := withMiddleware(authz, impl.ListNodeVersions)(ctx, drip.ListNodeVersionsRequestObject{ - NodeId: *node.Id, - }) - require.NoError(t, err, "should return created node version") - require.IsType(t, drip.ListNodeVersions200JSONResponse{}, res) - found := false - for _, nv := range res.(drip.ListNodeVersions200JSONResponse) { - if *nv.Version == *nodeVersionExtractionSucceeded1.Version || - *nv.Version == *nodeVersionExtractionSucceeded2.Version { - for k, v := range *nv.ComfyNodes { - found = true - ev := (*comfyNodes.Nodes)[k] - ev.ComfyNodeId = proto.String(k) - assert.Equal(t, ev, v) - } - } else { - assert.Empty(t, nv.ComfyNodes) - } - } - - assert.True(t, found) - }) - // Test case: Assert Algolia indexing t.Run("AssertAlgolia contains comfy nodes", func(t *testing.T) { indexed := impl.mockAlgolia.LastIndexedNodes diff --git a/integration-tests/node_test.go b/integration-tests/node_test.go index 8c9fa2f..969450e 100644 --- a/integration-tests/node_test.go +++ b/integration-tests/node_test.go @@ -7,6 +7,9 @@ import ( "registry-backend/config" "registry-backend/drip" + "registry-backend/ent" + "registry-backend/ent/node" + drip_logging "registry-backend/logging" authorization "registry-backend/server/middleware/authorization" "github.com/stretchr/testify/assert" @@ -118,18 +121,6 @@ func TestRegistryNode(t *testing.T) { assert.Equal(t, node.Repository, updatedResponse.Repository) }) - // Test reindexing nodes - t.Run("Reindex Nodes", func(t *testing.T) { - res, err := withMiddleware(authz, impl.ReindexNodes)(ctx, drip.ReindexNodesRequestObject{}) - require.NoError(t, err, "Node reindexing failed") - assert.IsType(t, drip.ReindexNodes200Response{}, res) - - time.Sleep(1 * time.Second) - nodes := impl.mockAlgolia.LastIndexedNodes - require.Equal(t, 1, len(nodes)) - assert.Equal(t, *node.Id, nodes[0].ID) - }) - // Test deleting the node t.Run("Delete Node", func(t *testing.T) { res, err := withMiddleware(authz, impl.DeleteNode)(ctx, drip.DeleteNodeRequestObject{ @@ -151,3 +142,113 @@ func TestRegistryNode(t *testing.T) { }) } + +func TestRegistryNodeReindex(t *testing.T) { + client, cleanup := setupDB(t, context.Background()) + defer cleanup() + + // Initialize server implementation and authorization middleware + impl := NewStrictServerImplementationWithMocks(client, &config.Config{}) + authz := authorization.NewAuthorizationManager( + client, impl.RegistryService, impl.NewRelicApp).AuthorizationMiddleware() + + // Setup user context and publisher + ctx, _ := setupTestUser(client) + ctx = drip_logging.SetupLogger().WithContext(ctx) + publisherId, err := setupPublisher(ctx, authz, impl) + require.NoError(t, err, "Failed to set up publisher") + + storeRandomNodes := func(t *testing.T, n int) []drip.Node { + nodes := make([]drip.Node, 0, n) + for i := 0; i < cap(nodes); i++ { + node := randomNode() + createResponse, err := withMiddleware(authz, impl.CreateNode)(ctx, drip.CreateNodeRequestObject{ + PublisherId: publisherId, + Body: node, + }) + require.NoError(t, err, "Node creation failed") + require.NotNil(t, createResponse, "Node creation returned nil response") + + createdNode := createResponse.(drip.CreateNode201JSONResponse) + nodes = append(nodes, drip.Node(createdNode)) + } + return nodes + } + + fetchIndexed := func(t *testing.T, ctx context.Context, indexedAfter time.Time, expectedLen int) []*ent.Node { + var indexed []*ent.Node + for { + require.NoError(t, ctx.Err()) + + indexed, err = client.Node.Query(). + Where(node.LastAlgoliaIndexTimeGT(indexedAfter)). + Where(node.LastAlgoliaIndexTimeLT(time.Now())). + All(ctx) + require.NoError(t, err) + + if len(indexed) < expectedLen { + time.Sleep(time.Second) + continue + } + + return indexed + } + } + + now := time.Now() + nodes := storeRandomNodes(t, 100) + + t.Run("AfterCreate", func(t *testing.T) { + indexed := fetchIndexed(t, ctx, now, len(nodes)) + assert.Equal(t, len(nodes), len(indexed)) + }) + + t.Run("AfterReindex", func(t *testing.T) { + now, batch := time.Now(), 9 + res, err := withMiddleware(authz, impl.ReindexNodes)(ctx, drip.ReindexNodesRequestObject{ + Params: drip.ReindexNodesParams{ + MaxBatch: &batch, + }, + }) + require.NoError(t, err, "Node reindexing failed") + assert.IsType(t, drip.ReindexNodes200Response{}, res) + + // check last_algolia_index_time + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + indexed := fetchIndexed(t, ctx, now, len(nodes)) + + assert.Equal(t, len(nodes), len(indexed), "should reindex all nodes") + assert.Len(t, impl.mockAlgolia.LastIndexedNodes, len(nodes)%batch, "should index to algolia partially") + + }) + + t.Run("AfterReindexWithMinAge", func(t *testing.T) { + batch, age := 8, 3*time.Second + time.Sleep(age) + + // add more nodes that will not be reindexed since it is too new + { + now := time.Now() + newNodes := storeRandomNodes(t, 20) + indexed := fetchIndexed(t, ctx, now, len(newNodes)) + assert.Equal(t, len(newNodes), len(indexed)) + } + + now = time.Now() + res, err := withMiddleware(authz, impl.ReindexNodes)(ctx, drip.ReindexNodesRequestObject{ + Params: drip.ReindexNodesParams{ + MaxBatch: &batch, + MinAge: &age, + }, + }) + require.NoError(t, err, "Node reindexing failed") + assert.IsType(t, drip.ReindexNodes200Response{}, res) + + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + indexed := fetchIndexed(t, ctx, now, len(nodes)) + assert.Equal(t, len(nodes), len(indexed), "should reindex some nodes") + assert.Len(t, impl.mockAlgolia.LastIndexedNodes, len(nodes)%batch, "should index to algolia partially") + }) +} diff --git a/logging/logging.go b/logging/logging.go index 1c800da..626e487 100644 --- a/logging/logging.go +++ b/logging/logging.go @@ -47,6 +47,7 @@ func SetupLogger() zerolog.Logger { return log } +// ReuseContextLogger returns a new context with the same logger as the given context func ReuseContextLogger(ctx context.Context, newCtx context.Context) context.Context { l := zerolog.Ctx(ctx) if l == nil { diff --git a/mapper/node_version.go b/mapper/node_version.go index 90f9792..f54cbfe 100644 --- a/mapper/node_version.go +++ b/mapper/node_version.go @@ -59,15 +59,6 @@ func DbNodeVersionToApiNodeVersion(dbNodeVersion *ent.NodeVersion) *drip.NodeVer downloadUrl = dbNodeVersion.Edges.StorageFile.FileURL } - var comfyNodes *map[string]drip.ComfyNode - if len(dbNodeVersion.Edges.ComfyNodes) > 0 { - cn := make(map[string]drip.ComfyNode, len(dbNodeVersion.Edges.ComfyNodes)) - for _, v := range dbNodeVersion.Edges.ComfyNodes { - cn[v.Name] = *DBComfyNodeToApiComfyNode(v) - } - comfyNodes = &cn - } - apiVersion := &drip.NodeVersion{ Id: &id, Version: &dbNodeVersion.Version, @@ -78,7 +69,6 @@ func DbNodeVersionToApiNodeVersion(dbNodeVersion *ent.NodeVersion) *drip.NodeVer Status: status, StatusReason: &dbNodeVersion.StatusReason, DownloadUrl: &downloadUrl, - ComfyNodes: comfyNodes, NodeId: &dbNodeVersion.NodeID, } return apiVersion diff --git a/openapi.yml b/openapi.yml index 37eab53..fb659c8 100644 --- a/openapi.yml +++ b/openapi.yml @@ -1274,6 +1274,20 @@ paths: operationId: reindexNodes tags: - Nodes + parameters: + - in: query + name: max_batch + description: Maximum number of nodes to send to algolia at a time + required: false + schema: + type: integer + - in: query + name: min_age + description: Minimum interval from the last time the nodes were indexed to algolia + required: false + schema: + type: string + x-go-type: time.Duration responses: '200': description: Reindex completed successfully. @@ -2234,10 +2248,6 @@ components: status_reason: type: string description: The reason for the status change. - comfy_nodes: - type: object - additionalProperties: - $ref: "#/components/schemas/ComfyNode" node_id: type: string description: The unique identifier of the node. diff --git a/server/implementation/registry.go b/server/implementation/registry.go index e6772ff..89d2a1f 100644 --- a/server/implementation/registry.go +++ b/server/implementation/registry.go @@ -1042,8 +1042,11 @@ func (s *DripStrictServerImplementation) ListAllNodeVersions( func (s *DripStrictServerImplementation) ReindexNodes(ctx context.Context, request drip.ReindexNodesRequestObject) (res drip.ReindexNodesResponseObject, err error) { defer tracing.TraceDefaultSegment(ctx, "DripStrictServerImplementation.ReindexNodes")() + // create new context with logger from original Context reindexCtx := drip_logging.ReuseContextLogger(ctx, context.Background()) - err = s.RegistryService.ReindexAllNodesBackground(reindexCtx, s.Client) + + err = s.RegistryService.ReindexAllNodesBackground( + reindexCtx, s.Client, request.Params.MaxBatch, request.Params.MinAge) if err != nil { log.Ctx(ctx).Error().Msgf("Failed to trigger reindex all nodes w/ err: %v", err) return drip.ReindexNodes500JSONResponse{Message: "Failed to trigger reindex nodes", Error: err.Error()}, nil diff --git a/services/registry/registry_svc.go b/services/registry/registry_svc.go index 10eb127..a40932b 100644 --- a/services/registry/registry_svc.go +++ b/services/registry/registry_svc.go @@ -325,7 +325,7 @@ func (s *RegistryService) CreateNode(ctx context.Context, client *ent.Client, pu return fmt.Errorf("failed to create node: %w", err) } - err = s.algolia.IndexNodes(ctx, createdNode) + err = s.indexNodes(ctx, tx.Client(), createdNode) if err != nil { return fmt.Errorf("failed to index node: %w", err) } @@ -669,6 +669,7 @@ func (s *RegistryService) CreateComfyNodes( nv, err := tx.NodeVersion.Query(). Where(nodeversion.VersionEQ(nodeVersion)). Where(nodeversion.NodeIDEQ(nodeID)). + WithComfyNodes(). ForUpdate(). Only(ctx) if err != nil { @@ -1049,7 +1050,7 @@ func (s *RegistryService) BanPublisher(ctx context.Context, client *ent.Client, return fmt.Errorf("fail to update nodes: %w", err) } - err = s.algolia.IndexNodes(ctx, nodes...) + err = s.indexNodes(ctx, tx.Client(), nodes...) if err != nil { return fmt.Errorf("failed to index node: %w", err) } @@ -1087,7 +1088,7 @@ func (s *RegistryService) BanNode(ctx context.Context, client *ent.Client, publi return fmt.Errorf("fail to ban node: %w", err) } - err = s.algolia.IndexNodes(ctx, n) + err = s.indexNodes(ctx, tx.Client(), n) if err != nil { return fmt.Errorf("failed to index node: %w", err) } @@ -1129,53 +1130,100 @@ func (s *RegistryService) AssertPublisherBanned(ctx context.Context, client *ent return nil } -func (s *RegistryService) ReindexAllNodes(ctx context.Context, client *ent.Client) error { - defer tracing.TraceDefaultSegment(ctx, "RegistryService.ReindexAllNodes")() - - log.Ctx(ctx).Info().Msgf("reindexing nodes") - nodes, err := s.decorateNodeQueryWithLatestVersion(client.Node.Query()).All(ctx) - if err != nil { - return fmt.Errorf("failed to fetch all nodes: %w", err) - } +// reindexLock is used to prevent multiple reindexing at the same time +var reindexLock = sync.Mutex{} - log.Ctx(ctx).Info().Msgf("reindexing %d number of nodes", len(nodes)) - err = s.algolia.IndexNodes(ctx, nodes...) - if err != nil { - return fmt.Errorf("failed to reindex all nodes: %w", err) - } +// ReindexAllNodesBackground initiates reindexing of all nodes in a background goroutine +func (s *RegistryService) ReindexAllNodesBackground( + ctx context.Context, client *ent.Client, maxBatch *int, minAge *time.Duration) error { + defer tracing.TraceDefaultSegment(ctx, "RegistryService.ReindexAllNodesBackground")() - var nvs []*ent.NodeVersion - for _, n := range nodes { - nvs = append(nvs, n.Edges.Versions...) + // Lock to prevent multiple reindexing operations running simultaneously + if !reindexLock.TryLock() { + return fmt.Errorf("another reindex is in progress") } + defer reindexLock.Unlock() - log.Ctx(ctx).Info().Msgf("reindexing %d number of node versions", len(nvs)) - err = s.algolia.IndexNodeVersions(ctx, nvs...) - if err != nil { - return fmt.Errorf("failed to reindex all node versions: %w", err) - } + // Start the reindexing process in a background goroutine + go func() { + err := s.ReindexAllNodes(ctx, client, maxBatch, minAge) + if err != nil { + log.Ctx(ctx).Err(err).Msg("failed to reindex all nodes in background") + } else { + log.Ctx(ctx).Info().Msg("reindexing nodes in background successful") + } + }() return nil } -var reindexLock = sync.Mutex{} +// ReindexAllNodes processes reindexing of all nodes to Algolia +func (s *RegistryService) ReindexAllNodes( + ctx context.Context, client *ent.Client, maxBatch *int, minAge *time.Duration) error { + defer tracing.TraceDefaultSegment(ctx, "RegistryService.ReindexAllNodes")() -func (s *RegistryService) ReindexAllNodesBackground(ctx context.Context, client *ent.Client) (err error) { - defer tracing.TraceDefaultSegment(ctx, "RegistryService.ReindexAllNodesBackground")() + log.Ctx(ctx).Info().Msg("starting node reindexing") + // Lock to prevent multiple reindexing operations running simultaneously if !reindexLock.TryLock() { return fmt.Errorf("another reindex is in progress") } defer reindexLock.Unlock() - go func() { - err = s.ReindexAllNodes(ctx, client) + nodeQuery := client.Node.Query().Order(node.ByID()) + + // Apply a batch limit if specified + if maxBatch != nil { + nodeQuery = nodeQuery.Limit(*maxBatch) + } + + // Filter nodes by minimum age if provided + if minAge != nil { + maxTime := time.Now().Add(-*minAge) + nodeQuery = nodeQuery.Where(node.Or( + node.LastAlgoliaIndexTimeLTE(maxTime), + node.LastAlgoliaIndexTimeIsNil(), + )) + } + + count, lastNodeID := 0, "" + + // Process nodes in batches + for { + // Fetch nodes with IDs greater than the last processed node ID + nodeQuery = nodeQuery.Where(node.IDGT(lastNodeID)) + nodes, err := s.decorateNodeQueryWithLatestVersion(nodeQuery).All(ctx) if err != nil { - log.Ctx(ctx).Err(err).Msg("failed to reindex all nodes in background") + return fmt.Errorf("failed to fetch nodes: %w", err) } - log.Ctx(ctx).Info().Msg("reindexing nodes in background succesful") - }() + // Break if no more nodes to process + if len(nodes) == 0 { + break + } + + // Update the last processed node ID and increment the count + count += len(nodes) + lastNodeID = nodes[len(nodes)-1].ID + + // Index the fetched nodes + if err = s.indexNodes(ctx, client, nodes...); err != nil { + return fmt.Errorf("failed to reindex nodes: %w", err) + } + + // Collect and reindex associated node versions + var nvs []*ent.NodeVersion + for _, n := range nodes { + nvs = append(nvs, n.Edges.Versions...) + } + log.Ctx(ctx).Info().Msgf("reindexing %d node versions", len(nvs)) + if err = s.algolia.IndexNodeVersions(ctx, nvs...); err != nil { + return fmt.Errorf("failed to reindex node versions: %w", err) + } + } + + // Log the completion of the reindexing process + log.Ctx(ctx).Info().Msgf("finished reindexing %d nodes", count) return nil } @@ -1191,12 +1239,36 @@ func (s *RegistryService) indexNodeWithLatestVersion( if err != nil { return nil, fmt.Errorf("failed to query node: %w", err) } - if err := s.algolia.IndexNodes(ctx, n); err != nil { + if err := s.indexNodes(ctx, client, n); err != nil { return nil, fmt.Errorf("failed to update node: %w", err) } return n, nil } +func (s *RegistryService) indexNodes(ctx context.Context, client *ent.Client, nodes ...*ent.Node) (err error) { + log.Ctx(ctx).Info().Msgf("indexing %d number of nodes", len(nodes)) + + err = s.algolia.IndexNodes(ctx, nodes...) + if err != nil { + return fmt.Errorf("failed to index %d number of nodes to algolia: %w", len(nodes), err) + } + + ids := []string{} + for _, n := range nodes { + ids = append(ids, n.ID) + } + + _, err = client.Node.Update(). + Where(node.IDIn(ids...)). + SetLastAlgoliaIndexTime(time.Now()). + Save(ctx) + if err != nil { + return fmt.Errorf("failed to update last algolia index time for %d number of nodes: %w", len(nodes), err) + } + + return +} + func (s *RegistryService) decorateNodeQueryWithLatestVersion(q *ent.NodeQuery) *ent.NodeQuery { return q.WithVersions(func(q *ent.NodeVersionQuery) { q.Modify(func(s *sql.Selector) {