首先,感谢任何人阅读这篇文章,并提供任何建议和帮助。这是非常感谢的。
我正在为我父亲的企业(专业承包商)开发一个小型的定制CRM (ouch),我的数据库使用Firestore。它应该是非常精益的,没有太多的“华丽”,而是流到他的专业承包业务,这是很难得到任何其他定制客户关系管理应用于他的过程。我已经走了很远,有了一个不错的规模实现,但现在遇到一些非常基本的问题,因为一切都在扩大。
我承认,只有在关系数据库方面有经验(而且也没有多少经验),在正确设置数据库结构时,我就会绞尽脑汁几次,并且遇到了一些关于Firestore的问题。我也是一个相当新手的开发人员,我觉得我正在处理一些超出我的范畴的事情。(但在这一年的旅程中,没有多少转机)
现在,我正在使用顶级集合作为我在这里展示的内容。最近,我开始在其他一些次要特性中使用子集合,并开始质疑我是否应该将它应用于任何事情。我预见到的一个大问题是,由于我想以多种方式进行查询,此时我已经消耗了将近100个复合索引。还有很多要添加,所以我需要减少我当前和未来的数据结构所需要的复合索引的数量。
因此,我可以肯定,我的数据模型可能有很大的缺陷,需要改进/优化/更改。(我不介意这样做,如果这是需要的,但我对“如何”感到迷茫)我不需要一个具体的解决方案,但也许只是一些指点,一般来说,什么方法是可用的。我想我可能缺少一个“啊哈”的时刻。如果我理解一个模式,我通常可以进一步应用于其他领域。
我将把我的“销售领导集”作为这篇文章的中心关注点,因为它有最多的查询变体。
因此,我有一个类似于这样的顶级集合结构,但也希望前缀,除了将ID写入其他文档之外,我还将将整个“客户”或“销售代表”对象/文档与其他文档“存储”在一起,并且我有云函数,在有更新时会遍历某些文档,等等(为了避免额外的读取,即当我读取SalesLead时,我不需要读取SalesRep和Customer文档,因为它们也是嵌套在SalesLead中的)
| /sales_reps //SalesReps Collection
| /docId //Document ID
| + salesRepId (document id)
| + firstName
| + lastName
| + other employee/salesRep related info etc.
| /customers //Customers Collection
| /docId //Document ID
| + customerId (document id)
| + firstName
| + lastName
| + address + other customer specific related info such as contact info (phone, email) etc.从逻辑上讲,销售线索当然是与客户联系在一起的(一对多,一个客户可以有多个线索)。下面提到的所有字段我需要能够“查询”和“筛选”
| /sales_leads //SalesLeads Collection
| /docId //Document ID
| + customerId (document id) <- this is what I would query by to look for leads for a specific customer
| + salesRepId (document id) <- this is what I would query by to look for leads for a specific sales Rep
| + status <- (String: "Open", "Sold", "Lost", "On Hold)
| + progress <- (String: "Started", "Appointment scheduled", "Estimates created", etc. etc., )
| + type <- (String: New Construction or Service/Repair)
| + jobTye <- (String: Different Types job Jobs related to what type of structures they are; 8-10 types right now)
| + reference <- (String: How the lead was referred to the company, i.e. Facebook, Google, etc. etc. );
| + many other (non queryable) data related to a lead, but not relevant here...SalesEstimates与一对多关系中的线索有关。(一条线索可以有很多的估计)但是估计与这次讨论并不完全相关,只是想把它包括进去。不过,我以非常类似的方式查询和过滤估计值。(类似的字段等)
| /sales_estimates //SalesEstimates Collection
| /docId //Document ID
| + salesLeadId (document id) <- this is what I would query by to look for estimates for a specific lead
| + customerId (document id) <- this is what I would query by to look for estimates for a specific customer
| + salesRepId (document id) <- this is what I would query by to look for estimates for a specific sales Rep
| + specific sales Lead related data etc....在客户端的“销售领先列表”中,我有一些下拉列表作为过滤器,其中包含值(即Sales ),但也有一个选项/值"All“来否定任何筛选。
因此,我将开始组装一个查询:
Query query = db.collection("sales_leads");
//Rep
if (!salesRepFilter.equals("All")) { //Typically only Managers/Supervisors woujld be able to see "all leads" whereas for a SalesRep this would be set on his own ID by default.
query = query = query.whereEqualTo("salesRepId", salesRepId);
}
//Lead Status (Open, Sold, Lost, On Hold)
if (!statusFilter.contains("All")) {
query = query.whereEqualTo("status", statusFilter);
}
//Lead Progress
if (!progressFilter.contains("All")) {
query = query.whereEqualTo("progress", progressFilter);
}
//Lead Type
if (!typeFilter.contains("All")) {
query = query.whereEqualTo("leadType", typeFilter);
}
//Job Type
if (!jobTypeFilter.contains("All")) {
query = query.whereArrayContains("jobTypes", jobTypeFilter);
}
//Reference
if (!referenceFilter.contains("All")) {
query = query.whereEqualTo("reference", referenceFilter);
}此外,我可能希望将整个查询简化为单个客户(这通常意味着跳过所有其他筛选器,并“显示该客户的所有线索”)。如果用户打开客户页/详细信息,单击“显示此客户的线索”之类的内容,则会发生这种情况。
//Filter by Customer (when entering my SalesLead List from a Customer Card/Page where user clicked on "Show Leads for this Customer")
if (filterByCustomer) {
query = query.whereEqualTo("customerId", customerFilter);
}
//And at last I want to be able to query the date Range (when the lead was created) and also sort by "oldest" or "newest"
//Date Range
query = query.whereGreaterThan("leadCreatedOnDate", filterFromDate);
.whereLessThan("leadCreatedOnDate", filterToDate;
//Sort Newest vs Oldest
if (sortByNewest) { //either newest or oldest
query = query.orderBy("leadCreatedOnDate", Query.Direction.ASCENDING);
} else {
query = query.orderBy("leadCreatedOnDate", Query.Direction.DESCENDING);
}这就完成了我对销售线索的查询。这一切现在都很有效,但我担心的是继续前进,并最终达到综合指数的极限。我没有确切的数字,但我可能只是为了收集sales_leads而娱乐25-30个复合索引。(哎呀!)
不仅有许多字段需要查询,所需的复合索引的数量乘以可能的筛选器集的组合。(呃)
我需要能够查询所有的线索,然后通过上面提到的字段过滤它们(在描述我的sales_leads集合时)。
因此,与其将所有这些集合保留为顶级集合,我猜我应该通过娱乐子集合来重构我的数据库,但我尝试用不同的方法对其进行建模,而且似乎总是碰壁。
我想我可以将"sales_leads“作为每个customer对象下的子集合,并且可以使用集合组查询来检索”所有线索“,但是这些都需要复合索引,对吧?因此,这将是一个可搜索的领域的权衡。(..hits墙.)
抱歉太长了。我希望它是可读的。我感谢任何帮助,反馈和投入。我处于一个非常焦虑和沮丧的境地。
如果这不起作用,我可能需要考虑专业咨询。
谢谢!
发布于 2020-11-06 16:32:17
以下是一些我认为会对你有帮助的事情。
首先,看AWS re:发明2018年:亚马逊DynamoDB深度潜水 on YouTube。它是关于DynamoDB的,但是DynamoDB是一个非常类似于Firestore的NoSQL数据库,这些概念普遍适用。在视频的中途,Rick以您这样的公司为例,您可能会惊讶地看到,他通过数据建模可以有效地减少查询数量。
第二,熟悉Firestore的索引合并。在像您这样的情况下,最好手动创建复合索引,或者至少手动审计它们,因为Firestore的自动索引并不保证组合索引的菜单是最有效的。请记住,复合索引是根据执行查询的顺序自动创建的,如果稍后执行一个查询(如果通过取消先前的索引可以更好地构造该查询),Firestore将不会为您返回并删除它--您必须这样做。
我对sales-lead查询消耗25-30个综合索引这一事实非常怀疑;考虑到文档中有多少字段被索引,这个数字在我看来太高了。当然,在你做任何事情之前--在看过视频并研究过索引合并之后--我会把注意力完全集中在这个集合上。您必须完全确定此集合需要消耗的组合索引的最大数量。也许可以创建一个虚拟集合,并对索引合并进行实验,并真正理解它是如何工作的,因为这本身就可以解决所有的问题。如果Firestore不能处理贵公司的用例,我会感到震惊的。
第三,不要害怕去篡改你的数据。NoSQL的基本前提实际上是去正规化--也就是说,数据存储实际上应该是您最不关心的,而计算/操作实际上应该是您最关心的问题。如果您可以通过在多个集合中的多个文档上复制数据来减少查询数量,那么如果选择达到200个复合索引,就必须这样做。
https://stackoverflow.com/questions/64717408
复制相似问题