Datastore查询嵌入实体的复合索引通用创建方案咨询
解决动态嵌入属性的索引问题
首先,我完全懂你的痛点——用户能自定义列表名称的情况下,挨个给isUnsubscribed.{listName}、methodAdded.{listName}这类动态嵌入属性建索引根本不现实,而且直接给父属性(比如isUnsubscribed)建索引又没法覆盖你这种带动态键的查询场景。
下面是几个可行的思路,你可以根据自己用的ORM/数据库来调整:
1. 重构数据结构(长期最优方案)
与其用动态键的嵌入属性,不如把列表相关信息改成数组嵌套实体,比如把Contacts实体的结构调整成这样:
// 重构后的Contacts实体示例 class Contacts { // 其他原有属性... /** @var ListSubscription[] */ private $listSubscriptions; } class ListSubscription { private $listName; private $isUnsubscribed; private $methodAdded; private $dateAdded; }
这种情况下,你只需要给listSubscriptions.listName、listSubscriptions.isUnsubscribed等属性创建复合索引,查询时写法会变成:
$query->filter('listSubscriptions.listName', '=', $list) ->filter('listSubscriptions.isUnsubscribed', '=', '0') ->filter('listSubscriptions.methodAdded', '=', 'Import') ->filter('listSubscriptions.dateAdded', '<', '13.01.2018');
这个结构不仅彻底解决了动态索引的问题,还更符合数据库的设计最佳实践,后续扩展也更灵活。
2. 数据库层面的动态索引(应急折中方案)
如果实在没法重构数据结构,部分数据库支持通配符索引或动态字段索引:
- 比如MongoDB可以给嵌入文档的所有键创建索引:
db.contacts.createIndex( { "isUnsubscribed.$**": 1 } ),不过这类索引会占用更多存储,要权衡性能和资源消耗。 - 要是用的是Doctrine这类ORM,原生不支持通配符索引,但你可以通过执行自定义数据库语句来创建底层索引,查询时保持原来的写法就行。
3. 预计算字段/索引视图(小众适配方案)
如果上面两种方案都没法落地,可以考虑加一个预计算字段或者创建索引视图,把所有列表的订阅信息扁平化存储。比如在Contacts里加一个allListSubscriptions字段,存所有列表的键值对数组,然后给这个字段建索引,查询时通过匹配这个字段来过滤。
不过这种方案需要额外维护字段的一致性,比如添加/更新订阅时同步更新这个字段,会增加一些开发和维护成本。
提醒:通配符索引看起来方便,但大多数ORM没有原生支持,还容易导致索引膨胀,所以优先推荐重构数据结构的方案。
内容的提问来源于stack exchange,提问作者Dimitar Rusev
相关产品推荐
相关产品推荐

