【问题标题】:Firebase Javascript / Typescript map compatibilityFirebase Javascript / Typescript 地图兼容性
【发布时间】:2020-02-10 23:50:22
【问题描述】:

当我尝试将地图写入 firebase 时,我遇到了这个错误:

FirebaseError: Function DocumentReference.set() called with invalid data. Unsupported field value: a custom Map object (found in field documentData.elements)

我想将地图而不是数组写入 firebase,以便能够在不覆盖整个数组的情况下合并数据集,而只添加或删除地图的条目。 但是,即使 firebase 文档说它支持地图,我也无法让地图正常工作。

我的界面是这样的:

export interface Element {
   uid:string;
   value:string;
}
export interface DocumentData {
    uid: string;
    elements: Map<string, Element>;
}

将它们写为文档不起作用,并因上述错误而失败。

let documentData = {uid:"test_uid", elements: new Map([{"one", "test_string"}],[{"two", "test_string"}]]);
...
...
            .doc(documentData.uid)
            .set({documentData, {merge: true});

有人知道如何将地图写入存储吗?这实际上是地图的问题还是我使用的设置错误?

【问题讨论】:

    标签: javascript typescript firebase google-cloud-firestore


    【解决方案1】:

    Firestore 仅接受包含要写入文档的字段和值的普通旧 JavaScript 对象。它不接受 ES6 Map 对象,这完全不是一回事。

    只需构建对象并将其传递给set():

    const data = {
        uid: "test_uid",
        elements: {
            one: "test_string",
            two, "test_string"
        }
    }
    
    set(data, {merge: true})
    

    我建议咨询documentation 以获取更多示例。 Java 支持地图对象,但 JavaScript 不支持。

    上面的代码会发生什么,它将更新两个字段,一个“元素”将是一个 Firestore 地图类型字段。

    【讨论】:

      【解决方案2】:

      我创建了一个将地图写入平面对象的方法,并添加了一个该成员是地图的字段,因此当从 firebase 获取它时,可以将其放回原始对象中,到目前为止效果很好。

      export function toFlatObject(data: any) {
          if (!data) {
              return null;
          }
      
          const obj: any = {};
          for (const [dataKey, dataValue] of Object.entries(data)) {
              if (dataValue instanceof Map) {
                  const mapObj: any = {};
                  dataValue.forEach((value, key) => {
                      Object.defineProperty(mapObj, key.toString(), {
                          value: interfaceDataToFlatObject(value),
                          writable: true,
                          enumerable: true,
                      });
                  });
                  Object.defineProperty(mapObj, "dataStructureIsMap", {
                      value: true,
                      writable: true,
                      enumerable: true,
                  });
                  Object.defineProperty(obj, dataKey.toString(), {
                      value: mapObj,
                      writable: true,
                      enumerable: true,
                  });
              } else if (dataValue instanceof Object) {
                  Object.defineProperty(obj, dataKey, {
                      value: interfaceDataToFlatObject(dataValue),
                      writable: true,
                      enumerable: true,
                  });
              } else {
                  Object.defineProperty(obj, dataKey, {
                      value: dataValue,
                      writable: true,
                      enumerable: true,
                  });
              }
          }
          return obj;
      }
      

      【讨论】:

        【解决方案3】:

        我知道我的解决方案有点晚了,但我开发了一个效果很好并想分享的解决方案。

        最初,我在搜索不相关的内容时发现了this GIT repository,它运行良好,但实施存在许多问题(请参阅问题列表)。最后,我通过扩展 Map() 类并从上面的 GIT 中借用了一些代码,创建了一个新的解决方案。

           class MyMap<K, V> extends Map<K, V> {
                
                /**
                 * Outputs the contents of the map to a JSON object
                 * 
                 * @returns {{[key: string]: V}} 
                 * @memberof MyMap
                 */
                public toJSON(): { [key: string]: V } {
                    const obj = {}
            
                    const getValue = (value: any): any => {
                        if (value instanceof MyMap) {
                            return value.toJSON()
                        } else if (Array.isArray(value)) {
                            return value.map(v => getValue(v))
                        } else {
                            return value
                        }
                    }
            
                    for (const [key, value] of this) {
                        obj[String(key)] = getValue(value)
                    }
                    return obj
                }
            }
        

        我对 Firestore 文档成功运行了以下测试:

        // Instance
        const mapTerms= new MyMap<string, string>()
        
        // Add some items (I had this within a loop)
        mapTerms.set(doc.id, doc.data().text)
        
        // Write is as a map object into Firestore
        await db.doc(<my doc path>).set({
            text: currentTermsOfUse,
            map: mapTerms.toJSON()
        })
        

        【讨论】:

          【解决方案4】:

          您可以使用自定义类型存储地图结构,而无需使用 es6 地图。 检查以下元素类型

          export type Element = {
            key: string
            name: string;
            type: string;
          }
          export type Elements = { [key: string]: Element }
          
          // To add element in the elements
          const element: Element = {
            key: 'key1',
            name: 'myName',
            type: 'myType',
          }
          const elements = { [`${element.key}`]: element };
          
          // To access an element in the elements using key
          const element1 = elements['key1'];
          
          // To access the first element in the elemenets
          const element2 = elements[Object.keys(elements)[0]];
          
          // store
          app.firestore().collection('a').add(elements);
          // or
          app.firestore().collection('a').doc('doc').set(elements, merge: true);
          

          【讨论】:

            猜你喜欢
            • 2018-05-26
            • 1970-01-01
            • 2011-03-22
            • 2018-07-29
            • 2015-12-19
            • 2019-09-18
            • 1970-01-01
            • 1970-01-01
            • 2017-03-31
            相关资源
            最近更新 更多