MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Konvertieren Sie Query Builder-Bedingungen in MongoDB-Operationen, einschließlich verschachtelter Arrays von Unterdokumenten

Nach mehr Forschung habe ich eine funktionierende Lösung. Vielen Dank an alle hilfreichen Antwortenden, die Einblicke gegeben haben.

Die Funktion nimmt eine Abfrage aus dem Angular Query Builder-Modul und konvertiert sie in eine MongoDB-Abfrage.

Angular Query Builder

  {
    "condition": "and",
    "rules": [{
      "field": "RecordType",
      "operator": "=",
      "value": "Item"
    }, {
      "condition": "or",
      "rules": [{
        "field": "Items.Title",
        "operator": "contains",
        "value": "book"
      }, {
        "field": "Project",
        "operator": "in",
        "value": ["5d0699380a2958e44503acfb", "5d0699380a2958e44503ad2a", "5d0699380a2958e44503ad18"]
      }]
    }]
  }

MongoDB-Abfrageergebnis

  {
    "$and": [{
      "RecordType": {
        "$eq": "Item"
      }
    }, {
      "$or": [{
        "Items.Title": {
          "$regex": "book",
          "$options": "i"
        }
      }, {
        "Project": {
          "$in": ["5d0699380a2958e44503acfb", "5d0699380a2958e44503ad2a", "5d0699380a2958e44503ad18"]
        }
      }]
    }]
  }

Code

/**
 * Convert a query object generated by UI to MongoDB query
 * @param query a query builder object generated by Angular2QueryBuilder module
 * @param model the model for the schema to query
 * return a MongoDB query
 * 
 */

apiCtrl.convertQuery = async (query, model) => {

  if (!query || !model) {
    return {};
  }

  const conditions = { "and": "$and", "or": "$or" };
  const operators = {
    "=": "$eq",
    "!=": "$ne",
    "<": "$lt",
    "<=": "$lte",
    ">": "$gt",
    ">=": "gte",
    "in": "$in",
    "not in": "$nin",
    "contains": "$regex"
  };

  // Get Mongoose schema type instance of a field
  const getSchemaType = (field) => {
    return model.schema.paths[field] ? model.schema.paths[field].instance : false;
  }

  // Map each rule to a MongoDB query
  const mapRule = (rule) => {

    let field = rule.field;
    let value = rule.value;

    if (!value) {
      value = null;
    }

    // Get schema type of current field
    const schemaType = getSchemaType(rule.field);

    // Check if schema type of current field is ObjectId
    if (schemaType === 'ObjectID' && value) {
      // Convert string value to MongoDB ObjectId
      if (Array.isArray(value)) {
        value.map(val => new ObjectId(val));
      } else {
        value = new ObjectId(value);
      }
    // Check if schema type of current field is Date
    } else if (schemaType === 'Date' && value) {
      // Convert string value to ISO date
      console.log(value);
      value = new Date(value);
    }

    console.log(schemaType);
    console.log(value);

    // Set operator
    const operator = operators[rule.operator] ? operators[rule.operator] : '$eq';

    // Create a MongoDB query
    let mongoDBQuery;

    // Check if operator is $regex
    if (operator === '$regex') {
      // Set case insensitive option
      mongoDBQuery = {
        [field]: {
          [operator]: value,
          '$options': 'i'
        }
      };
    } else {
      mongoDBQuery = { [field]: { [operator]: value } };
    }

    return mongoDBQuery;

  }

  const mapRuleSet = (ruleSet) => {

    if (ruleSet.rules.length < 1) {
      return;
    }

    // Iterate Rule Set conditions recursively to build database query
    return {
      [conditions[ruleSet.condition]]: ruleSet.rules.map(
        rule => rule.operator ? mapRule(rule) : mapRuleSet(rule)
      )
    }
  };

  let mongoDbQuery = mapRuleSet(query);

  return mongoDbQuery;

}