检查Flutter Firestore中的字段是否已存在

21

我有一个名为company的集合。 所有公司都将以我屏幕截图中的方式存储。

当我添加另一家公司时,我想检查name是否已经存在。 如何执行检查?

在这里,“Nova”和“Tradetech”是两个公司。

当我尝试使用字段name:“nova”再次添加“Nova”时,我希望显示通知:“Company already exists!”.

screenshot

5个回答

31

我用以下代码解决了这个问题,感谢您的帮助!

在下面的代码中,我使用了以下方法来查找:

1)文档是否存在?

2)是否存在?

3)是否存在?

SIMPLE METHOD
//////////////////////////////////////////////////////////////////////


import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';

String myText1 = 'temp1';
String myText2 = 'temp2';
String myText3 = 'temp3';
String myText4 = 'temp4';
String myText5 = 'temp5';
String myText6 = 'temp6';
StreamSubscription<DocumentSnapshot> subscription;
final DocumentReference documentReference =
    Firestore.instance.document("company/Nova");

class Clean extends StatefulWidget {
  @override
  _CleanState createState() => _CleanState();
}

class _CleanState extends State<Clean> {
  @override
  void initState() {
    super.initState();
    subscription = documentReference.snapshots().listen((datasnapshot) {
      //FINDING A SPECIFICDOCUMENT IS EXISTING INSIDE A COLLECTION

      if (datasnapshot.exists) {
        setState(() {
          myText1 = "Document exist";
        });
      } else if (!datasnapshot.exists) {
        setState(() {
          myText2 = "Document not exist";
        });
      }

      //FINDING A SPECIFIC KEY IS EXISTING INSIDE A DOCUMENT

      if (datasnapshot.data.containsKey("name")) {
        setState(() {
          myText3 = "key exists";
        });
      } else if (!datasnapshot.data.containsKey("name")) {
        setState(() {
          myText4 = "key not exists";
        });
      }


      //FINDING A SPECIFIC VALUE IS EXISTING INSIDE A DOCUMENT

      if (datasnapshot.data.containsValue("nova")) {
        setState(() {
          myText5 = "value exists";
        });
      } else if (!datasnapshot.data.containsValue("nova")) {
        setState(() {
          myText6 = "value not exists";
        });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        new Text(myText1),
        new Text(myText2),
        new Text(myText3),
        new Text(myText4),
        new Text(myText5),
        new Text(myText6),
      ],
    );
  }
}

我旧的复杂方法基于我的现有代码 ////////////////////////////////////////////////////////

概念

它有一个搜索栏,当你输入时,它会显示公司名称,即现有或不存在。

一个Card和一个RaisedButton。我在Firestore中使用小写字母以避免搜索错误。我强制TextFormField输出为小写字母,使用toLowercase()。您可以将其更改为自己的文本格式。

代码

//if the name is not existing it will show a raised button so u can clcik on that to 
//go to a COMPANY ADDING PAGE,otherwise it will only show a **CARD** so that you  
//can't go to the next page to add your company


//code:

import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';
import './fullscreen.dart';

const blue = 0xFF3b78e7;
String filter = '';
StreamSubscription<DocumentSnapshot> subscription;

final TextEditingController _usercontroller = new TextEditingController();

class CheckAvail extends StatefulWidget {
  @override
  HomeState createState() => HomeState();
}

class HomeState extends State<CheckAvail> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomPadding: false,
      body: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        children: <Widget>[
// CHILD1
          new Flexible(
            child: StreamBuilder(
              stream: Firestore.instance
                  .collection('company')
                  .where('name', isGreaterThanOrEqualTo: filter.toLowerCase())
                  .limit(1)
                  .snapshots(),
              builder: (BuildContext context,
                  AsyncSnapshot<QuerySnapshot> snapshot) {
                if (!snapshot.hasData) {
                  return new Column(
                    children: <Widget>[
                      new Card(
                        elevation: 5.0,
                        child: new Image.asset('assets/progress.gif'),
                      )
                    ],
                  );
                } else {
                  return FirestoreListView1(documents: snapshot.data.documents);
                }
              },
            ),
          ),

          new Card(
            elevation: 0.0,
            color: Colors.white,
            shape: new RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(60.0)),
            child: Container(
              padding: new EdgeInsets.only(left: 8.0),
              child: new TextField(
                controller: _usercontroller,
                onChanged: (String z) {
                  setState(() {
                    filter = z;
                  });
                },
                decoration: const InputDecoration(
                  hintText: "Search...",
                  hintStyle: TextStyle(
                      fontFamily: 'roboto',
                      color: Colors.black38,
                      fontSize: 16.0,
                      letterSpacing: -0.500),
                  fillColor: Colors.white,
                  border: InputBorder.none,
                ),
              ),
            ),
          ),
        ],
      ),
      backgroundColor: Color(blue),
    );
  }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class FirestoreListView1 extends StatelessWidget {
  final List<DocumentSnapshot> documents;
  FirestoreListView1({this.documents});
  @override
  Widget build(BuildContext context1) {
    return ListView.builder(
        itemCount: documents.length,
        padding: new EdgeInsets.all(1.0),
        itemBuilder: (BuildContext context1, int index) {
          String name = documents[index].data['name'];
          if (name.contains(filter.toLowerCase()) &&
              name.length == filter.length) {
            return new Container(
              padding: new EdgeInsets.only(top: 45.0),
              child: new Card(
                  child: new Text(
                      "Error:Already a Company Exists with this name\nTry another name")),
            );
          } else {
            return (filter.length >= 1)
                ? new Container(
                    padding: new EdgeInsets.only(top: 15.0),
                    child: new RaisedButton(
                      onPressed: () => Navigator.push(
                          context1,
                          new MaterialPageRoute(
                              builder: (context1) => new NextPage(
                                    value1: name,
                                  ))),
                      disabledColor: Colors.white,
                      child: new Text(
                        "Good!You can use this company name",
                      ),
                    ),
                  )
                : new Container(padding: new EdgeInsets.only(top: 250.0),
                child: new Card(child: new Text("CHECK IF YOUR COMPANY NAME \n           AVAILABLE OR NOT",style: new TextStyle(fontSize: 20.0),)),
              );
          }
        });
  }
}

@creativecreatorormaybenot 谢谢兄弟,我明白了,我不会再这样做了!! - Rajesh
有一个看起来像勾号的按钮,一旦你按下它,它就会变成绿色。那就是你应该在投票下面使用的按钮来接受一个答案。例如,你可以接受自己的答案来标记它解决了问题,或者标记别人的答案。 - creativecreatorormaybenot
@creativecreatorormaybenot 抱歉,网络出了问题,我昨晚已经回复你了,但现在不可用,无论如何,谢谢。 - Rajesh
1
使用这段代码datasnapshot.data.containsKey("name")总是会输出错误:Unhandled Exception: NoSuchMethodError: The method 'containsKey' was called on null. - Michael Tolsma
3
datasnapshot.data.containsKey("name") 似乎已被废弃...错误:方法“containsKey”未为类型“Object”定义。 - genericUser
显示剩余3条评论

16

您可以使用 where 查询 来仅接收具有该名称的文档,然后检查您是否获得了文档。以下是一个 async 示例方法,可执行您想了解的内容。

示例方法

Future<bool> doesNameAlreadyExist(String name) async {
  final QuerySnapshot result = await Firestore.instance
    .collection('company')
    .where('name', isEqualTo: name)
    .limit(1)
    .getDocuments();
  final List<DocumentSnapshot> documents = result.documents;
  return documents.length == 1;
}

正如您所看到的,我仅接收文档,其中“名称”字段与给定的“名称”相匹配。我还添加了limit(1)以确保我不会不必要地检索超过一个文档(理论上永远不会发生),然后我只需检查company集合中所有文档的长度是否等于1。如果等于1,则已经存在具有该名称的公司,否则不存在。

您也可以删除limit(1)并使检查为documents.length>1,这也可以工作,但可能会检索不必要的文档。

示例实现

@override
Widget build(BuildContext context) {
  return FutureBuilder(
    future: doesNameAlreadyExist('nova'),
    builder: (context, AsyncSnapshot<bool> result) {
      if (!result.hasData)
        return Container(); // future still needs to be finished (loading)
      if (result.data) // result.data is the returned bool from doesNameAlreadyExists
        return Text('A company called "Nova" already exists.');
      else
        return Text('No company called "Nova" exists yet.');
    },
  );
}

这里,我没有显示一个错误信息,使用示例方法也很容易实现。然而,使用了某些小部件的build方法。在对话框中,这是可以工作的,但为了保持简单和易懂,我决定这样做。 FutureBuilder接收doesNameAlreadyExist,在这种情况下使用您问题中的名称"Nova",最终将返回一个Text小部件,说明该名称是否已存在。

要小心

where查询区分大小写。这意味着如果您输入例如"noVa"而不是"nova",则检查将无法正常工作。由于这可能对您很重要,因此您可以使用此不错的方法,创建一个不区分大小写的额外字段,例如所有字母都是小写,然后您就可以像这样轻松查询:

.where('name_insensitive', isEqualTo: name.toLowerCase())

2
@creativecreatormaybenot,非常感谢您的概念。我已经使用了您的概念和我的现有代码,然后使用字符串长度进行准确查找。为了避免大小写敏感的问题,我总是将数据存储为小写字母,并在我的文本字段中使用“toLowercase”。 - Rajesh
在Flutter中,我们可以使用区分大小写和startswith方法吗? - Jay Mungara
唯一的问题是,如果我正在使用此逻辑进行插入(如果不存在),可能会出现竞争条件...因此,我应该事务性地查找然后插入。有什么线索可以一次性完成所有操作吗? - MobileMon
谢谢,这是一个简单且最容易的一个。 - Dhananjay pathak
在公司不存在的情况下,您需要进行2次调用,Firestore是否提供了仅在“名称”不存在时插入的功能?如果已存在,则抛出异常? - Paritosh Agrawal
但我认为他是在询问字段是否存在,而不是值? - Francisca Mkina

6
final QuerySnapshot result =
    await Firestore.instance.collection('users').where('nickname', isEqualTo: 
    nickname).getDocuments();

final List < DocumentSnapshot > documents = result.documents;

if (documents.length > 0) { 

  //exists

} else {  

  //not exists

}

1
当documents.length == 0时,它不应该是另一种方式吗?//不存在另外//存在? - Panduranga Rao Sadhu
1
是的,我已经编辑过了,如果(documents.length > 0){ //存在} else { //不存在} - Gabriel Almeida

5

使用这个函数:

snapshot.data!.data()!.containsKey('key_name')

检查文档中是否存在字段。

PS. 我刚在自己的RN代码中使用了这个方法,它可以正常工作。


1

我知道我来晚了。 为未来的用户发布。 尝试这个:

DocumentReference datab = db.collection("Company").document("Nova");

datab.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
    @Override
    public void onSuccess(DocumentSnapshot documentSnapshot) {

        if(documentSnapshot.contains("name"))
        {
            Toast.makeText(YourActivity.this, "Child exixts.", Toast.LENGTH_SHORT).show();

        }
        else
            Toast.makeText(YourActivity.this, "Doesnt exits.", Toast.LENGTH_SHORT).show();


    }
});

1
抱歉!这是一个过时的问题,谢谢你的回答。顺便说一下,可以简单地完成如下操作 >>> await documentref.get().the((doc)async{ int age = doc.data['age]})// 如果 age == null,则该字段不可用。 - Rajesh
这不是Flutter,而是本地的Android代码! - Kamal Panara

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接