Source: lib/db/reads.js

import { ObjectID } from "bson";
import {
  getCommentsCollection,
  getDreamsCollection,
  getInboxCollection,
  getStarsCollection,
  getUsersCollection,
} from "../mongodb";
import { decryptDream } from "../transformations";

/**
 * Get a dream by its id and optionally filter the results
 *
 * @param {string} dreamId The dream id
 */
export async function getDreamById(dreamId) {
  const collection = await getDreamsCollection();

  const cursor = collection.find({ _id: ObjectID(dreamId) }).limit(1);

  const result = await cursor.toArray();

  await cursor.close();

  if (result.lenght === 0) {
    return null;
  }

  const data = result[0];

  if (data.visibility === "private") {
    const decryptedDream = decryptDream(data.dream);

    return {
      ...data,
      dream: decryptedDream,
    };
  }

  return data;
}

/**
 * @private
 */
export async function getDreams(userEmail) {
  const [user, collection] = await Promise.all([
    getUserByEmail(userEmail),
    getDreamsCollection(),
  ]);

  const cursor = collection
    .find({ userId: ObjectID(user._id) })
    .sort({ _id: -1 })
    .limit(200);

  const rawResult = await cursor.toArray();

  await cursor.close();

  if (rawResult.lenght === 0) {
    return null;
  }

  const result = [];

  for (let data of rawResult) {
    if (data.visibility === "private") {
      data = {
        ...data,
        dream: decryptDream(data.dream),
      };
    }

    result.push(data);
  }

  return result;
}

/**
 * @private
 */
export async function getDreamRecords(userEmail) {
  const [user, collection] = await Promise.all([
    getUserByEmail(userEmail),
    getDreamsCollection(),
  ]);

  const cursor = collection
    .find({ userId: ObjectID(user._id) })
    .sort({ _id: -1 })
    .limit(200);

  const rawResult = await cursor.toArray();

  await cursor.close();

  if (rawResult.lenght === 0) {
    return null;
  }

  const result = [];

  for (let data of rawResult) {
    result.push({
      createdAt: data.createdAt,
      dreamId: data._id,
      wordFrequency: data.wordFrequency,
      characterCount: data.characterCount,
    });
  }

  return result;
}

/**
 * @public
 */
export async function getLatestPublicDreams() {
  const collection = await getDreamsCollection();

  const cursor = collection
    .find({ visibility: { $in: ["public", "anonymous"] } })
    .sort({ _id: -1 })
    .limit(200);

  const result = await cursor.toArray();

  await cursor.close();

  if (result.lenght === 0) {
    return null;
  }

  return result;
}

/**
 * @public
 */
export async function getUserByEmail(userEmail) {
  const collection = await getUsersCollection();

  const cursor = collection.find({ email: userEmail }).limit(1);

  const result = await cursor.toArray();

  await cursor.close();

  if (result.lenght === 0) {
    return null;
  }

  return result[0];
}

/**
 * @public
 */
export async function getUserById(userId) {
  const collection = await getUsersCollection();

  const cursor = collection.find({ _id: ObjectID(userId) }).limit(1);

  const result = await cursor.toArray();

  await cursor.close();

  if (result.lenght === 0) {
    return null;
  }

  return result[0];
}

export async function searchDreams(query) {
  const collection = await getDreamsCollection();

  const result = await collection
    .aggregate([
      {
        $search: {
          index: "default",
          text: {
            query,
            fuzzy: {
              maxExpansions: 10,
              prefixLength: 3,
            },
            path: "dream.text",
          },
        },
      },
    ])
    .toArray();

  return result;
}

export async function getComments(dreamId) {
  const [collection, dream] = await Promise.all([
    getCommentsCollection(),
    getDreamById(dreamId),
  ]);

  if (!dream) {
    console.error({
      name: "Dream miss",
      message: "No dream found to append comment",
    });

    return;
  }

  const cursor = collection
    .find({ dreamId: ObjectID(dreamId) })
    .sort({ _id: 1 });

  const result = await cursor.toArray();

  if (result.lenght === 0) {
    return null;
  }

  return result;
}

export async function getCommentsByUserId(userId) {
  const collection = await getCommentsCollection();

  const cursor = collection
    .find({ userId: ObjectID(userId) })
    .sort({ _id: -1 });

  const result = await cursor.toArray();

  if (result.lenght === 0) {
    return null;
  }

  return result;
}

export async function getStars(dreamId) {
  const [collection, dream] = await Promise.all([
    getStarsCollection(),
    getDreamById(dreamId),
  ]);

  if (!dream) {
    console.error({
      name: "Dream miss",
      message: "No dream found to star",
    });

    return;
  }

  const cursor = collection
    .find({ dreamId: ObjectID(dreamId) })
    .sort({ _id: 1 });

  const result = await cursor.toArray();

  if (result.lenght === 0) {
    return null;
  }

  return result;
}

export async function getStarsByUserEmail(userEmail) {
  const collection = await getStarsCollection();

  const cursor = collection.find({ userEmail }).sort({ _id: -1 });

  const result = await cursor.toArray();

  if (result.lenght === 0) {
    return null;
  }

  return result;
}

/**
 * @private
 */
export async function getStarredDreams(userEmail) {
  const stars = await getStarsByUserEmail(userEmail);

  const dreams = await Promise.all(
    stars.map((star) => getDreamById(star.dreamId))
  );

  if (dreams.lenght === 0) {
    return null;
  }

  return dreams.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
}

// TODO: transform this into a searchUsers method
export async function getUsers() {
  const collection = await getUsersCollection();

  const cursor = collection.find({}).sort({ _id: -1 }).limit(200);

  const result = await cursor.toArray();

  await cursor.close();

  if (result.lenght === 0) {
    return null;
  }
}

/**
 * Gets the inbox data of a user
 *
 * @param {string} userEmail The user email
 */
export async function getInbox(userEmail) {
  const collection = await getInboxCollection();

  const cursor = collection
    .find({ dreamOwnerUserEmail: userEmail })
    .sort({ _id: -1 });

  const result = await cursor.toArray();

  if (result.lenght === 0) {
    return null;
  }

  return result;
}

/**
 * Gets the count of unread messages in the inbox
 *
 * @param {string} userEmail The user email
 */
export async function getInboxCount(userEmail) {
  const collection = await getInboxCollection();

  const result = await collection
    .find({ dreamOwnerUserEmail: userEmail, read: false })
    .count();

  return result;
}

/**
 * Gets a comment on a publication by its id
 *
 * @param {string} commentId The comment id
 */
export async function getCommentById(commentId) {
  const collection = await getCommentsCollection();

  const cursor = collection.find({ _id: ObjectID(commentId) }).limit(1);

  const result = await cursor.toArray();

  await cursor.close();

  if (result.lenght === 0) {
    return null;
  }

  const data = result[0];

  return data;
}

/**
 * Gets a star from a specific publication by its id
 *
 * @param {string} userEmail The user email
 * @param {string} postId The publication id
 */
export async function getStar(userEmail, postId) {
  const collection = await getStarsCollection();

  const cursor = collection
    .find({ userEmail: userEmail, dreamId: ObjectID(postId) })
    .limit(1);

  const result = await cursor.toArray();

  await cursor.close();

  if (result.lenght === 0) {
    return null;
  }

  const data = result[0];

  return data;
}

/**
 * Checks whether a user has commented on a post or not
 *
 * @param {string} userName The user name
 * @param {string} postId The post id
 * @returns {Promise<boolean>}
 */
export async function hasCommentedOnDream(userName, postId) {
  const collection = await getCommentsCollection();

  return (
    (await collection
      .find({ userName: userName, dreamId: ObjectID(postId) })
      .count()) > 0
  );
}

/**
 * Checks if the AI has commented on a dream
 *
 * @param {string} postId The post id
 * @returns {Promise<boolean>}
 */
export async function hasAiCommentedOnDream(postId) {
  const [hasCommented, legacyHasCommented] = await Promise.all([
    hasCommentedOnDream("Sonia", postId),
    hasCommentedOnDream("Sonio", postId),
  ]);

  return hasCommented || legacyHasCommented;
}