<commit_before>/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "_" }] */
/* globals isNumeric_, wrapValue_ */

/**
 * This callback type is called `queryCallback`.
 *  Callback should utilize the Query parameter to send a request and return the response.
 *
 * @callback queryCallback
 * @param {object} query the Structured Query to utilize in the query request {@link FirestoreQuery_}
 * @returns [object] response of the sent query
 */

/**
 * An object that acts as a Query to be a structured query.
 * Chain methods to update query. Must call .execute to send request.
 *
 * @constructor
 * @private
 * @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery Firestore Structured Query}
 * @param {string} from the base collection to query
 * @param {queryCallback} callback the function that is executed with the internally compiled query
 */
var FirestoreQuery_ = function (from, callback) {
  const this_ = this

  // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#Operator_1 FieldFilter Operator}
  const fieldOps = {
    '==': 'EQUAL',
    '===': 'EQUAL',
    '<': 'LESS_THAN',
    '<=': 'LESS_THAN_OR_EQUAL',
    '>': 'GREATER_THAN',
    '>=': 'GREATER_THAN_OR_EQUAL'
  }

  // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#Operator_2 FieldFilter Operator}
  const unaryOps = {
    'nan': 'IS_NAN',
    'null': 'IS_NULL'
  }

  // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#FieldReference Field Reference}
  const fieldRef = function (field) {
    return {fieldPath: field}
  }
  const filter = function (field, operator, value) {
    // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#FieldFilter Field Filter}
    if (operator in fieldOps) {
      return {
        fieldFilter: {
          field: fieldRef(field),
          op: fieldOps[operator],
          value: wrapValue_(value)
        }
      }
    }

    // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#UnaryFilter Unary Filter}
    if (operator.toLowerCase() in unaryOps) {
      return {
        unaryFilter: {
          field: fieldRef(field),
          op: unaryOps[operator]
        }
      }
    }
    throw new Error('Invalid Operator given ' + operator)
  }

  const query = {}
  if (from) {
    query.from = [{collectionId: from}]<commit_msg>Allow Query Filter values of null, undefined, or NaN to coalesce into a Unary Filter.<commit_after>/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "_" }] */
/* globals isNumeric_, wrapValue_ */

/**
 * This callback type is called `queryCallback`.
 *  Callback should utilize the Query parameter to send a request and return the response.
 *
 * @callback queryCallback
 * @param {object} query the Structured Query to utilize in the query request {@link FirestoreQuery_}
 * @returns [object] response of the sent query
 */

/**
 * An object that acts as a Query to be a structured query.
 * Chain methods to update query. Must call .execute to send request.
 *
 * @constructor
 * @private
 * @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery Firestore Structured Query}
 * @param {string} from the base collection to query
 * @param {queryCallback} callback the function that is executed with the internally compiled query
 */
var FirestoreQuery_ = function (from, callback) {
  const this_ = this

  // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#Operator_1 FieldFilter Operator}
  const fieldOps = {
    '==': 'EQUAL',
    '===': 'EQUAL',
    '<': 'LESS_THAN',
    '<=': 'LESS_THAN_OR_EQUAL',
    '>': 'GREATER_THAN',
    '>=': 'GREATER_THAN_OR_EQUAL'
  }

  // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#Operator_2 FieldFilter Operator}
  const unaryOps = {
    'nan': 'IS_NAN',
    'null': 'IS_NULL'
  }

  // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#FieldReference Field Reference}
  const fieldRef = function (field) {
    return {fieldPath: field}
  }
  const filter = function (field, operator, value) {
    // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#FieldFilter Field Filter}
    if (operator in fieldOps) {
      if (value == null) { // Covers null and undefined values
        operator = 'null'
      } else if (isNaN(value)) { // Covers NaN
        operator = 'nan'
      } else {
        return {
          fieldFilter: {
            field: fieldRef(field),
            op: fieldOps[operator],
            value: wrapValue_(value)
          }
        }
      }
    }

    // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#UnaryFilter Unary Filter}
    if (operator.toLowerCase() in unaryOps) {
      return {
        unaryFilter: {
          field: fieldRef(field),
          op: unaryOps[operator]
        }
      }
    }
    throw new Error('Invalid Operator given ' + operator)
  }

  const query = {}
  if (from) {
    query.from = [{collectionId: from}]
{'fraction_non_alphanumeric': 0.12717309145880573, 'fraction_numerical': 0.005291005291005291, 'mean_word_length': 3.1974623314829502, 'pattern_counts': {'":': 2, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 12, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3881048', 'n_tokens_mistral': 1624, 'n_tokens_neox': 1598, 'n_words': 427}