/* * Generate the changes for a specific list of doc ID's, only documents accesible to the user will generate * results */ func (h *handler) sendChangesForDocIds(userChannels base.Set, explicitDocIds []string, options db.ChangesOptions) (error, bool) { // Subroutine that creates a response row for a document: first := true var lastSeq uint64 = 0 //rowMap := make(map[uint64]*changesRow) rowMap := make(map[uint64]*db.ChangeEntry) createRow := func(doc db.IDAndRev) *db.ChangeEntry { row := &db.ChangeEntry{ID: doc.DocID} // Fetch the document body and other metadata that lives with it: body, _, _, _, flags, sequence, err := h.db.GetRevAndChannels(doc.DocID, doc.RevID, false) if err != nil { base.LogTo("Changes", "Unable to get changes for docID %v", doc.DocID) return nil } if sequence <= options.Since.Seq { return nil } if body == nil { return nil } doc.RevID = body["_rev"].(string) doc.Sequence = sequence changes := make([]db.ChangeRev, 1) changes[0] = db.ChangeRev{"rev": doc.RevID} row.Changes = changes row.Seq = db.SequenceID{Seq: doc.Sequence} row.SetBranched((flags & channels.Branched) != 0) if options.IncludeDocs || options.Conflicts { h.db.AddDocToChangeEntry(row, options) } return row } h.setHeader("Content-Type", "application/json") h.setHeader("Cache-Control", "private, max-age=0, no-cache, no-store") h.response.Write([]byte("{\"results\":[\r\n")) var keys base.Uint64Slice for _, docID := range explicitDocIds { row := createRow(db.IDAndRev{DocID: docID, RevID: "", Sequence: 0}) if row != nil { rowMap[row.Seq.Seq] = row keys = append(keys, row.Seq.Seq) } } //Write out rows sorted by sequenceID keys.Sort() for _, k := range keys { if first { first = false } else { h.response.Write([]byte(",")) } h.addJSON(rowMap[k]) lastSeq = k if options.Limit > 0 { options.Limit-- if options.Limit == 0 { break } } } s := fmt.Sprintf("],\n\"last_seq\":%d}\n", lastSeq) h.response.Write([]byte(s)) h.logStatus(http.StatusOK, "OK") return nil, false }
/* * Generate the changes for a specific list of doc ID's, only documents accesible to the user will generate * results */ func (h *handler) sendChangesForDocIds(userChannels base.Set, explicitDocIds []string, options db.ChangesOptions) (error, bool) { // Subroutine that creates a response row for a document: first := true var lastSeq uint64 = 0 //rowMap := make(map[uint64]*changesRow) rowMap := make(map[uint64]*db.ChangeEntry) createRow := func(doc db.IDAndRev) *db.ChangeEntry { row := &db.ChangeEntry{ID: doc.DocID} // Fetch the document body and other metadata that lives with it: populatedDoc, body, err := h.db.GetDocAndActiveRev(doc.DocID) if err != nil { base.LogTo("Changes", "Unable to get changes for docID %v, caused by %v", doc.DocID, err) return nil } if populatedDoc.Sequence <= options.Since.Seq { return nil } if body == nil { return nil } changes := make([]db.ChangeRev, 1) changes[0] = db.ChangeRev{"rev": body["_rev"].(string)} row.Changes = changes row.Seq = db.SequenceID{Seq: populatedDoc.Sequence} row.SetBranched((populatedDoc.Flags & ch.Branched) != 0) var removedChannels []string if deleted, _ := body["_deleted"].(bool); deleted { row.Deleted = true } userCanSeeDocChannel := false if h.user == nil || h.user.Channels().Contains(ch.UserStarChannel) { userCanSeeDocChannel = true } else if len(populatedDoc.Channels) > 0 { //Do special _removed/_deleted processing for channel, removal := range populatedDoc.Channels { //Doc is tagged with channel or was removed at a sequence later that since sequence if removal == nil || removal.Seq > options.Since.Seq { //if the current user has access to this channel if h.user.CanSeeChannel(channel) { userCanSeeDocChannel = true //If the doc has been removed if removal != nil { removedChannels = append(removedChannels, channel) if removal.Deleted { row.Deleted = true } } } } } } if !userCanSeeDocChannel { return nil } row.Removed = base.SetFromArray(removedChannels) if options.IncludeDocs || options.Conflicts { h.db.AddDocInstanceToChangeEntry(row, populatedDoc, options) } return row } h.setHeader("Content-Type", "application/json") h.setHeader("Cache-Control", "private, max-age=0, no-cache, no-store") h.response.Write([]byte("{\"results\":[\r\n")) var keys base.Uint64Slice for _, docID := range explicitDocIds { row := createRow(db.IDAndRev{DocID: docID, RevID: "", Sequence: 0}) if row != nil { rowMap[row.Seq.Seq] = row keys = append(keys, row.Seq.Seq) } } //Write out rows sorted by sequenceID keys.Sort() for _, k := range keys { if first { first = false } else { h.response.Write([]byte(",")) } h.addJSON(rowMap[k]) lastSeq = k if options.Limit > 0 { options.Limit-- if options.Limit == 0 { break } } } s := fmt.Sprintf("],\n\"last_seq\":%d}\n", lastSeq) h.response.Write([]byte(s)) h.logStatus(http.StatusOK, "OK") return nil, false }