Repository: leancloud/js-realtime-sdk (js)

Issue:
对话支持 and 与 or 查询
https://leanticket.cn/tickets/23304

需要暴露类型存储 SDK 中的 `Query.and` 与 `Query.or` 的 API。并且 `ConversationQuery` 也需要 export 出来。

Pull request description:
feat(query): add "and", "or" function to ConversationQuery
resolve #631

Patch:
diff --git a/realtime.d.ts b/realtime.d.ts
index 47315fb..6c17845 100644
--- a/realtime.d.ts
+++ b/realtime.d.ts
@@ -119,6 +119,12 @@ declare class IMClient extends EventEmitter<ClientEvent | SharedEvent> {
 }
 
 declare class ConversationQuery<T extends ConversationBase> {
+  static and<U extends ConversationBase>(
+    ...queries: ConversationQuery<U>[]
+  ): ConversationQuery<U>;
+  static or<U extends ConversationBase>(
+    ...queries: ConversationQuery<U>[]
+  ): ConversationQuery<U>;
   addAscending(key: string): this;
   addDescending(key: string): this;
   ascending(key: string): this;
diff --git a/src/conversation-query.js b/src/conversation-query.js
index 26b8155..a0879bd 100644
--- a/src/conversation-query.js
+++ b/src/conversation-query.js
@@ -25,6 +25,37 @@ export default class ConversationQuery {
     );
   }
 
+  /**
+   * 构造一个用 AND 连接所有查询的 ConversationQuery
+   * @param {...ConversationQuery} queries
+   * @return {ConversationQuery}
+   */
+  static and(...queries) {
+    if (queries.length < 2) {
+      throw new Error('The queries must contain at least two elements');
+    }
+    if (!queries.every(q => q instanceof ConversationQuery)) {
+      throw new Error(
+        'The element of queries must be an instance of ConversationQuery'
+      );
+    }
+    const combined = new ConversationQuery(queries[0]._client);
+    combined._where.$and = queries.map(q => q._where);
+    return combined;
+  }
+
+  /**
+   * 构造一个用 OR 连接所有查询的 ConversationQuery
+   * @param  {...ConversationQuery} queries
+   * @return {ConversationQuery}
+   */
+  static or(...queries) {
+    const combined = ConversationQuery.and(...queries);
+    combined._where.$or = combined._where.$and;
+    delete combined._where.$and;
+    return combined;
+  }
+
   /**
    * Create a ConversationQuery
    * @param  {IMClient} client
diff --git a/src/im.js b/src/im.js
index 7c05d64..592ebd5 100644
--- a/src/im.js
+++ b/src/im.js
@@ -45,3 +45,10 @@ export {
    */
   MessageParser,
 };
+
+export {
+  /**
+   * @see ConversationQuery
+   */
+  default as ConversationQuery,
+} from './conversation-query';


Test patch:
diff --git a/test/conversation-query.js b/test/conversation-query.js
index aa8a818..5f5de9c 100644
--- a/test/conversation-query.js
+++ b/test/conversation-query.js
@@ -1,7 +1,11 @@
 import ConversationQuery from '../src/conversation-query';
 import Message, { MessageStatus } from '../src/messages/message';
 
-import { EXISTING_ROOM_ID, createRealtime } from './configs';
+import {
+  createRealtime,
+  EXISTING_ROOM_ID,
+  NON_EXISTING_ROOM_ID,
+} from './configs';
 
 describe('ConversationQuery', () => {
   // divided to 2 parts since there is query quota for every single client
@@ -386,5 +390,32 @@ describe('ConversationQuery', () => {
       ]).then(conversations => {
         conversations[0].should.be.exactly(conversations[1]);
       }));
+    describe('and & or', () => {
+      it('should fail when provide less than 2 parameters', () =>
+        (() => ConversationQuery.and()).should.throw());
+      it('should fail when parameters is not a ConversationQuery instance', () =>
+        (() => ConversationQuery.and(0, 0)).should.throw());
+      it('should return 0 conversations', () => {
+        const goodQuery = new ConversationQuery(client);
+        goodQuery.equalTo('objectId', EXISTING_ROOM_ID);
+        const badQuery = new ConversationQuery(client);
+        badQuery.equalTo('objectId', NON_EXISTING_ROOM_ID);
+        return ConversationQuery.and(goodQuery, badQuery)
+          .find()
+          .then(conversations => conversations.length.should.be.equal(0));
+      });
+      it(`should return a conversation which id is ${EXISTING_ROOM_ID}`, () => {
+        const goodQuery = new ConversationQuery(client);
+        goodQuery.equalTo('objectId', EXISTING_ROOM_ID);
+        const badQuery = new ConversationQuery(client);
+        badQuery.equalTo('objectId', NON_EXISTING_ROOM_ID);
+        ConversationQuery.or(goodQuery, badQuery)
+          .find()
+          .then(conversations => {
+            conversations.length.should.be.equal(1);
+            conversations[0].id.should.be.equal(EXISTING_ROOM_ID);
+          });
+      });
+    });
   });
 });