簡體   English   中英

使用雲 function 時,來自 firestore 的時間戳被轉換為 Map

[英]Timestamp from firestore gets converted to a Map when using cloud function

所以我在雲 Firestore 中有一個Timestamp 我正在使用雲 function 從 firestore 檢索數據到 flutter。但是JSON時間戳格式設置為 map,因此我無法將其用作時間戳。 如何將其再次轉換為時間戳?
這就是我將時間戳加載到 firestore 的方式。

var reference = Firestore.instance.collection('posts');
      reference.add({
        'postTitle': this.title,
        'timestamp': DateTime.now(),
        'likes': {},
        'ownerId': userId,
      })

要檢索數據,這是代碼:

 factory Post.fromJSON(Map data){
    return Post(
      timestamp: data['timestamp'],
    );
  }
List<Post> _generateFeed(List<Map<String, dynamic>> feedData) {
    List<Post> listOfPosts = [];

    for (var postData in feedData) {
      listOfPosts.add(Post.fromJSON(postData));
    }

    return listOfPosts;
  }

但這會返回一個錯誤。

I/flutter (17271): The following assertion was thrown building FutureBuilder<DocumentSnapshot>(dirty, state:
I/flutter (17271): _FutureBuilderState<DocumentSnapshot>#1536b):
I/flutter (17271): type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Timestamp'

這是我的雲 function。
getFeed.ts

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';

export const getFeedModule = function(req, res){
    const uid = String(req.query.uid);

    async function compileFeedPost(){
        const following = await getFollowing(uid, res)as any;

        let listOfPosts = await getAllPosts(following, res);

        listOfPosts = [].concat.apply([], listOfPosts);

        res.send(listOfPosts);
    }

    compileFeedPost().then().catch();
}

async function getAllPosts(following, res) {
    let listOfPosts = [];

    for (let user in following){
        listOfPosts.push( await getUserPosts(following[user], res));
    }
    return listOfPosts;
}

function getUserPosts(userId, res){
    const posts = admin.firestore().collection("posts").where("ownerId", "==", userId).orderBy("timestamp")

    return posts.get()
    .then(function(querySnapshot){
        let listOfPosts = [];

        querySnapshot.forEach(function(doc){
            listOfPosts.push(doc.data());
        });

        return listOfPosts;
    })
}

function getFollowing(uid, res){
    const doc = admin.firestore().doc(`user/${uid}`)
    return doc.get().then(snapshot => {
        const followings = snapshot.data().followings;

        let following_list = [];

        for (const following in followings){
            if (followings[following] === true){
                following_list.push(following);
            }
        }
        return following_list;
    }).catch(error => {
        res.status(500).send(error)
    })
}

雲 function index.ts

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import { getFeedModule } from "./getFeed"
admin.initializeApp();

export const getFeed = functions.https.onRequest((req, res) => {
    getFeedModule(req, res);
})


由此調用

_getFeed() async {
    print("Starting getFeed");
    FirebaseUser user = await FirebaseAuth.instance.currentUser();

    SharedPreferences prefs = await SharedPreferences.getInstance();

    String userId = user.uid;
    var url =
        'https://us-central1-jaluk-quiz.cloudfunctions.net/getFeed?uid=' + userId;
    var httpClient = HttpClient();

    List<QuizViewer>listOfPosts;
    String result;
    try {
      var request = await httpClient.getUrl(Uri.parse(url));
      var response = await request.close(); 
      if (response.statusCode == HttpStatus.ok) {
        String json = await response.transform(utf8.decoder).join();
        prefs.setString("feed", json);
        List<Map<String, dynamic>> data =
            jsonDecode(json).cast<Map<String, dynamic>>();
        listOfPosts = _generateFeed(data);
        result = "Success in http request for feed";
      } else {
        result =
            'Error getting a feed: Http status ${response.statusCode} | userId $userId';
      }
    } catch (exception) {
      result = 'Failed invoking the getFeed function. Exception: $exception';
    }
    print(result);

    setState(() {
      feedData = listOfPosts;
    });
  }

實際上,在使用 Cloud 函數時,時間戳會作為普通 Map 返回。 但是如果您使用 Firebase SDK,它會返回Timestamp對象。 我使用以下函數來處理這兩種情況:

DateTime dateTimeFromTimestamp(dynamic val) {
  Timestamp timestamp;
  if (val is Timestamp) {
    timestamp = val;
  } else if (val is Map) {
    timestamp = Timestamp(val['_seconds'], val['_nanoseconds']);
  }
  if (timestamp != null) {
    return timestamp.toDate();
  } else {
    print('Unable to parse Timestamp from $val');
    return null;
  }
}

json_annotation庫完美配合:

  @JsonKey(
      fromJson: dateTimeFromTimestamp,
      toJson: dateTimeToTimestamp,
      nullable: true)
  final DateTime subscriptionExpiryDate;

如果您正在處理已序列化為具有秒和納秒組件的對象的 Timestamp,您可以使用這些組件創建一個具有new Timestamp(seconds, nanoseconds)的新Timestamp對象。

您可以使用轉換來接收 DateTime,如下所示:

class TimestampConverter implements JsonConverter<DateTime, dynamic> {
  const TimestampConverter();

  @override
  DateTime fromJson(dynamic data) {
    Timestamp timestamp;
    if (data is Timestamp) {
      timestamp = data;
    } else if (data is Map) {
      timestamp = Timestamp(data['_seconds'], data['_nanoseconds']);
    }
    return timestamp?.toDate();
  }

  @override
  Map<String, dynamic> toJson(DateTime dateTime) {
    final timestamp = Timestamp.fromDate(dateTime);
    return {
      '_seconds': timestamp.seconds,
      '_nanoseconds': timestamp.nanoseconds,
    };
  }
}

然后像這樣標記你的模型領域:

@TimestampConverter() DateTime createdAt

安德烈的回答非常好。 這是一個 JS/Typescript 改編,封裝在一個類中:

import app from 'firebase/app'
import 'firebase/firestore'

import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en'

// Add locale-specific relative date/time formatting rules.
TimeAgo.addLocale(en)

// Adapted from Andrey Gordeev's answer at: 
// https://stackoverflow.com/questions/56245156/timestamp-from-firestore-gets-converted-to-a-map-when-using-cloud-function

class MyClass {
    timeAgo: TimeAgo

    constructor() {
        this.timeAgo = new TimeAgo('en-US')
    }

    getTimeText = (timeObject: any) => {
        // Convert to time text once it's of type firestore.Timestamp
        const getTextFromTimestamp = (timestamp: app.firestore.Timestamp) => {
            return this.timeAgo.format(timestamp.toDate())

        }
        if (timeObject instanceof app.firestore.Timestamp) {
            // Check if Timestamp (accessed from client SDK)
            return getTextFromTimestamp(timeObject)
        } else if (Object.prototype.toString.call(timeObject) === '[object Object]') {
            // Check if it's a Map
            const seconds = timeObject['_seconds']
            const nanoseconds = timeObject['_nanoseconds']
            if (seconds && nanoseconds) {
                const timestamp = new app.firestore.Timestamp(seconds, nanoseconds)
                return getTextFromTimestamp(timestamp)
            }
        }
        console.log('Couldn\'t parse time', timeObject)
        // Fallback
        return 'some time ago'
    }
}
DateTime? dateTimeFromTimeStamp(dynamic data) {
  Timestamp? timestamp;

  if (data is Timestamp) {
    timestamp = data;
  } else if (data is Map) {
    timestamp = Timestamp(data['_seconds'], data['_nanoseconds']);
  }
  return timestamp?.toDate();
}

Map<String, dynamic>? dateTimeToTimeStamp(DateTime? dateTime) {
  final timestamp = Timestamp.fromDate(dateTime ?? DateTime.now());
  return {
    '_seconds': timestamp.seconds,
    '_nanoseconds': timestamp.nanoseconds,
  };
}

然后像這樣使用它:

  @JsonKey(
      name: "creation_date",
      fromJson: dateTimeFromTimeStamp,
      toJson: dateTimeToTimeStamp)
  DateTime? creationDate;

我通過將時間戳作為字符串發送來解決了我的問題。

"timestamp": DateTime.now().toString()

因為現在我的時間戳現在在字符串中,所以我從 JSON 中獲取確切的時間戳作為字符串。
現在我所做的是使用一個名為timeago的 flutter 插件將其轉換為 time ago 格式,例如:“10 mins ago”

Text(timeago.format(DateTime.parse(timestamp)).toString);

我偶然發現了這個問題,因為我試圖弄清楚為什么我的雲函數沒有正確解析時間戳(並將其返回給調用者)。 實際上,在記錄時,我注意到我的日期字段顯示為Timestamp { _seconds: N, _nanoseconds: NN }

解決方案是在使用它之前簡單地將必要的字段轉換為 Timestamp 類。 否則會返回地圖:

const date = <field> as admin.firestore.Timestamp
print(expiryObj); //{_nanoseconds: 748000000, _seconds: 1612641862}

Timestamp tempstamp = Timestamp(expiryObj['_seconds'], expiryObj['_nanoseconds']);
DateTime expiryDateTime = tempstamp.toDate();

print(expiryDateTime); //2021-02-06 15:04:22.748

(導入導入'包:cloud_firestore/cloud_firestore.dart';)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM