如何在Azure CosmosDB中为Graph数据库选择分区键

问题描述

我正在使用Azure CosmosDB,更具体地说是使用Gremlin API,并且对于选择什么作为分区键我有些困惑。

实际上,由于我使用的是图形数据,因此并非所有的顶点都遵循相同的数据模式。如果选择的属性并非所有顶点都相同,则Azure将不允许我存储没有分区键值的顶点。问题在于,它们共有的唯一属性public class ChatsFragment extends Fragment { private RecyclerView mRecyclerView; private UserAdapterChat mUserAdapterChat; private List<User> mUsers; private List<Chatlist> mChatList; private FirebaseUser mFirebaseUser; private TextView mNoMessages; @Override public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_chats,container,false); mFirebaseUser = FirebaseAuth.getInstance().getCurrentUser(); mNoMessages = v.findViewById(R.id.no_messages); mRecyclerView = v.findViewById(R.id.recycler_view); mRecyclerView.setHasFixedSize(true); linearlayoutmanager linearlayoutmanager = new linearlayoutmanager(getContext()); mRecyclerView.setLayoutManager(linearlayoutmanager); mChatList = new ArrayList<>(); mUserAdapterChat = new UserAdapterChat(getContext(),mUsers,false); mRecyclerView.setAdapter(mUserAdapterChat); mUsers = new ArrayList<>(); EditText search_users = v.findViewById(R.id.search_bar); search_users.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s,int start,int count,int after) { } @Override public void onTextChanged(CharSequence s,int before,int count) { searchUsers(s.toString().toLowerCase()); } @Override public void afterTextChanged(Editable s) { } }); DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Chatlist").child(mFirebaseUser.getUid()); ChildEventListener childEventListener = new ChildEventListener() { @Override public void onChildAdded(@NonNull DataSnapshot snapshot,@Nullable String s) { Chatlist chatlist = snapshot.getValue(Chatlist.class); mChatList.add(chatlist); mUserAdapterChat.notifyDataSetChanged(); if (mChatList.size() == 0) { mNoMessages.setVisibility(View.VISIBLE); } else { mNoMessages.setVisibility(View.GONE); } } @Override public void onChildChanged(@NonNull DataSnapshot dataSnapshot,@Nullable String s) { chatList(); } @Override public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) { } @Override public void onChildMoved(@NonNull DataSnapshot dataSnapshot,@Nullable String s) { chatList(); } @Override public void onCancelled(@NonNull DatabaseError databaseError) { } }; reference.addChildEventListener(childEventListener); /* DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Chatlist").child(mFirebaseUser.getUid()); reference.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { mChatList.clear(); for (DataSnapshot snapshot : dataSnapshot.getChildren()) { Chatlist chatlist = snapshot.getValue(Chatlist.class); mChatList.add(chatlist); } if (mChatList.size() == 0) { mNoMessages.setVisibility(View.VISIBLE); } else { mNoMessages.setVisibility(View.GONE); chatList(); } } @Override public void onCancelled(@NonNull DatabaseError databaseError) { } }); */ Task<InstanceIdResult> task = FirebaseInstanceId.getInstance().getInstanceId(); task.addOnCompleteListener(task1 -> { if (task1.isSuccessful()) { String token = task1.getResult().getToken(); updatetoken(token); } else { Exception exception = task1.getException(); Log.d("TOKEN",exception.getMessage()); } }); chatList(); return v; } private void updatetoken(String token) { DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Tokens"); Token token1 = new Token(token); reference.child(mFirebaseUser.getUid()).setValue(token1); } private void searchUsers(String s) { Query query = FirebaseDatabase.getInstance().getReference("Users").orderByChild("username").startAt(s).endAt(s + "\uf8ff"); query.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { mUsers.clear(); for (DataSnapshot snapshot : dataSnapshot.getChildren()) { User user = snapshot.getValue(User.class); if (s.length() == 0) { mUsers.clear(); } else { if (user != null) { if (!user.getId().equals(mFirebaseUser.getUid())) { mUsers.add(user); mNoMessages.setVisibility(View.GONE); } } } } mUserAdapterChat = new UserAdapterChat(getContext(),false); mUserAdapterChat.notifyDataSetChanged(); mRecyclerView.setAdapter(mUserAdapterChat); } @Override public void onCancelled(@NonNull DatabaseError databaseError) { } }); } private void chatList() { mUsers = new ArrayList<>(mChatList.size()); DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Users"); reference.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { mUsers.clear(); Collections.sort(mChatList,ChatsFragment.this::compare); for (DataSnapshot snapshot : dataSnapshot.getChildren()) { User user = snapshot.getValue(User.class); for (int i = 0; i < mChatList.size(); i++) { if (mFirebaseUser != null && user != null) { if (!user.getId().equals(mFirebaseUser.getUid()) && user.getId().equals(mChatList.get(i).receiver)) { ensureSize((ArrayList<?>) mUsers,mChatList.size()); mUsers.set(i,user); } } } } mUserAdapterChat = new UserAdapterChat(getContext(),true); mUserAdapterChat.notifyDataSetChanged(); mRecyclerView.setAdapter(mUserAdapterChat); } @Override public void onCancelled(@NonNull DatabaseError databaseError) { } }); } public static void ensureSize(ArrayList<?> list,int size) { list.ensureCapacity(size); while (list.size() < size) { list.add(null); } } public int compare(Chatlist o1,Chatlist o2) { return o1.getTimestamp() < o2.getTimestamp() ? 1 : (o1.getTimestamp() == o2.getTimestamp() ? 0 : -1); } } ,但是Azure不允许将此属性用作分区键。

这是否意味着我需要创建一个所有顶点共同具有的属性?那不是扼杀了图形数据的目的吗?还是我错过了什么?

例如,对于我来说,我想对一个对象及其零件建模。每个对象和每个部分都有一个属性/id。将此属性用作分区键,或创建专用于分区目的的新属性/identificationNumber会更好吗?我担心的是,如果我选择/partitionKey作为分区键,并且如果将来我的数据模型必须发展,如果我必须在没有/identificationNumber的情况下对新对象进行建模,那么我将不得不人为地将此属性添加到数据模型的这些对象中,可能会引起一些混乱。

解决方法

如果没有明显的现有属性可以使用,则创建专用属性来用作synthetic partition key是一个好习惯。这种方法可以缓解某些对象中没有/identificationNumber的情况,因为在这种情况下,您可以将其他值分配为partitionKey。由于/identificationNumber是不变的,因此将来也可以灵活地重构partitionKey

我们不必担心“人工属性”,因为这是使用分区数据库所固有的。它不需要向用户公开,但是开发人员需要了解Cosmos与传统DB有所不同。还可以通过将所有数据复制到新的容器中来迁移到新的分区键,这在后悔的最坏情况下。最好是最好的猜测开始研究项目,看看事情如何进行,也许迭代不同的想法来比较性能等。