Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add createTime to Document. #6781

Merged
merged 10 commits into from Dec 2, 2022
22 changes: 5 additions & 17 deletions packages/firestore/src/local/local_documents_view.ts
Expand Up @@ -46,7 +46,6 @@ import { FieldMask } from '../model/field_mask';
import {
calculateOverlayMutation,
mutationApplyToLocalView,
MutationType,
PatchMutation
} from '../model/mutation';
import { Overlay } from '../model/overlay';
Expand Down Expand Up @@ -92,7 +91,7 @@ export class LocalDocumentsView {
.getOverlay(transaction, key)
.next(value => {
overlay = value;
return this.getBaseDocument(transaction, key, overlay);
return this.remoteDocumentCache.getEntry(transaction, key);
})
.next(document => {
if (overlay !== null) {
Expand Down Expand Up @@ -424,11 +423,11 @@ export class LocalDocumentsView {
if (originalDocs.get(key)) {
return PersistencePromise.resolve();
}
return this.getBaseDocument(transaction, key, overlay).next(
doc => {
return this.remoteDocumentCache
.getEntry(transaction, key)
.next(doc => {
modifiedDocs = modifiedDocs.insert(key, doc);
}
);
});
}
)
.next(() =>
Expand Down Expand Up @@ -550,15 +549,4 @@ export class LocalDocumentsView {
return results;
});
}

/** Returns a base document that can be used to apply `overlay`. */
private getBaseDocument(
transaction: PersistenceTransaction,
key: DocumentKey,
overlay: Overlay | null
): PersistencePromise<MutableDocument> {
return overlay === null || overlay.mutation.type === MutationType.Patch
? this.remoteDocumentCache.getEntry(transaction, key)
: PersistencePromise.resolve(MutableDocument.newInvalidDocument(key));
}
}
43 changes: 35 additions & 8 deletions packages/firestore/src/model/document.ts
Expand Up @@ -101,6 +101,13 @@ export interface Document {
*/
readonly readTime: SnapshotVersion;

/**
* The timestamp at which the document was created. This value increases
ehsannas marked this conversation as resolved.
Show resolved Hide resolved
* monotonically when a document is deleted then recreated. It can also be
* compared to `createTime` of other documents and the `readTime` of a query.
*/
readonly createTime: SnapshotVersion;

/** The underlying data of this document or an empty value if no data exists. */
readonly data: ObjectValue;

Expand Down Expand Up @@ -163,6 +170,7 @@ export class MutableDocument implements Document {
private documentType: DocumentType,
public version: SnapshotVersion,
public readTime: SnapshotVersion,
public createTime: SnapshotVersion,
public data: ObjectValue,
private documentState: DocumentState
) {}
Expand All @@ -175,8 +183,9 @@ export class MutableDocument implements Document {
return new MutableDocument(
documentKey,
DocumentType.INVALID,
SnapshotVersion.min(),
SnapshotVersion.min(),
/* version */ SnapshotVersion.min(),
/* readTime */ SnapshotVersion.min(),
/* createTime */ SnapshotVersion.min(),
ObjectValue.empty(),
DocumentState.SYNCED
);
Expand All @@ -189,13 +198,15 @@ export class MutableDocument implements Document {
static newFoundDocument(
documentKey: DocumentKey,
version: SnapshotVersion,
createTime: SnapshotVersion,
value: ObjectValue
): MutableDocument {
return new MutableDocument(
documentKey,
DocumentType.FOUND_DOCUMENT,
version,
SnapshotVersion.min(),
/* version */ version,
/* readTime */ SnapshotVersion.min(),
/* createTime */ createTime,
value,
DocumentState.SYNCED
);
Expand All @@ -209,8 +220,9 @@ export class MutableDocument implements Document {
return new MutableDocument(
documentKey,
DocumentType.NO_DOCUMENT,
version,
SnapshotVersion.min(),
/* version */ version,
/* readTime */ SnapshotVersion.min(),
/* createTime */ SnapshotVersion.min(),
ObjectValue.empty(),
DocumentState.SYNCED
);
Expand All @@ -228,8 +240,9 @@ export class MutableDocument implements Document {
return new MutableDocument(
documentKey,
DocumentType.UNKNOWN_DOCUMENT,
version,
SnapshotVersion.min(),
/* version */ version,
/* readTime */ SnapshotVersion.min(),
/* createTime */ SnapshotVersion.min(),
ObjectValue.empty(),
DocumentState.HAS_COMMITTED_MUTATIONS
);
Expand All @@ -243,6 +256,18 @@ export class MutableDocument implements Document {
version: SnapshotVersion,
value: ObjectValue
): MutableDocument {
// If a document is switching state from being an invalid or deleted
// document to a valid (FOUND_DOCUMENT) document, either due to receiving an
// update from Watch or due to applying a local set mutation on top
// of a deleted document, our best guess about its createTime would be the
// version at which the document transitioned to a FOUND_DOCUMENT.
if (
this.createTime.isEqual(SnapshotVersion.min()) &&
(this.documentType === DocumentType.NO_DOCUMENT ||
this.documentType === DocumentType.INVALID)
) {
this.createTime = version;
}
this.version = version;
this.documentType = DocumentType.FOUND_DOCUMENT;
this.data = value;
Expand Down Expand Up @@ -340,6 +365,7 @@ export class MutableDocument implements Document {
this.documentType,
this.version,
this.readTime,
this.createTime,
this.data.clone(),
this.documentState
);
Expand All @@ -350,6 +376,7 @@ export class MutableDocument implements Document {
`Document(${this.key}, ${this.version}, ${JSON.stringify(
this.data.value
)}, ` +
`{createTime: ${this.createTime}}), ` +
`{documentType: ${this.documentType}}), ` +
`{documentState: ${this.documentState}})`
);
Expand Down
31 changes: 27 additions & 4 deletions packages/firestore/src/remote/serializer.ts
Expand Up @@ -404,7 +404,8 @@ export function toDocument(
return {
name: toName(serializer, document.key),
fields: document.data.value.mapValue.fields,
updateTime: toTimestamp(serializer, document.version.toTimestamp())
updateTime: toTimestamp(serializer, document.version.toTimestamp()),
createTime: toTimestamp(serializer, document.createTime.toTimestamp())
};
}

Expand All @@ -415,8 +416,19 @@ export function fromDocument(
): MutableDocument {
const key = fromName(serializer, document.name!);
const version = fromVersion(document.updateTime!);
// If we read a document from persistence that is missing createTime, it's due
// to older SDK versions not storing this information. In such cases, we'll
// set the createTime to zero. This can be removed in the long term.
const createTime = document.createTime
? fromVersion(document.createTime)
: SnapshotVersion.min();
const data = new ObjectValue({ mapValue: { fields: document.fields } });
const result = MutableDocument.newFoundDocument(key, version, data);
const result = MutableDocument.newFoundDocument(
key,
version,
createTime,
data
);
if (hasCommittedMutations) {
result.setHasCommittedMutations();
}
Expand All @@ -435,8 +447,11 @@ function fromFound(
assertPresent(doc.found.updateTime, 'doc.found.updateTime');
const key = fromName(serializer, doc.found.name);
const version = fromVersion(doc.found.updateTime);
const createTime = doc.found.createTime
? fromVersion(doc.found.createTime)
: SnapshotVersion.min();
const data = new ObjectValue({ mapValue: { fields: doc.found.fields } });
return MutableDocument.newFoundDocument(key, version, data);
return MutableDocument.newFoundDocument(key, version, createTime, data);
}

function fromMissing(
Expand Down Expand Up @@ -502,10 +517,18 @@ export function fromWatchChange(
);
const key = fromName(serializer, entityChange.document.name);
const version = fromVersion(entityChange.document.updateTime);
const createTime = entityChange.document.createTime
? fromVersion(entityChange.document.createTime)
: SnapshotVersion.min();
const data = new ObjectValue({
mapValue: { fields: entityChange.document.fields }
});
const doc = MutableDocument.newFoundDocument(key, version, data);
const doc = MutableDocument.newFoundDocument(
key,
version,
createTime,
data
);
const updatedTargetIds = entityChange.targetIds || [];
const removedTargetIds = entityChange.removedTargetIds || [];
watchChange = new DocumentWatchChange(
Expand Down