1

I've got a web api written using expressjs and mongoosejs.

The main schema in the app contains a subdocument, permissions, which contains array fields. Those arrays contain ids of users who can perform an action on that document.

I'm trying to limit results of a query on that collection by the values in the read subdocument array field:

Here's the main schema:

var MainSchema = new Schema({
    uri: { type: String, required: false },
    user: { type: String, required: false },
    groups: [String],           
    tags: [String],
    permissions: {
        read: [String],
        update: [String],
        delete: [String]
    }
});

I want to return documents that have a specific value in the permissions.read array or where that array is empty.

My code doesn't throw an error, but doesn't limit the results; I still get documents that don't match the given value, and aren't empty.

Here's what I've got:

var MainModel = mongoose.model('MainSchema', MainSchema);

app.get('/api/search', function (req, res) {
    var query = MainModel.find({'uri': req.query.uri }); 

    // This works fine
    if (req.query.groups) {
        query.where('groups').in(req.query.groups);
    }
    else if (req.query.user) {
        query.where('user').equals(req.query.user);
    }

    // How can I return only documents that match req.query.user or ""?
    query.where('permissions.read').in([req.query.user, ""]);

    query.exec(function (err, results) {
      if (!err) {
        return res.send(results);
      } else {
        return console.log(err);
      }
    });
});

I have a hunch that the where clause is not testing the value of each of the elements of permissions.read against each of the values of the array passed to the in clause.

Thanks.

EDIT: Here's a document that shouldn't be returned, but is (note, permissions.read array includes a value that's not the current user's ID):

{
    "user": "[email protected]",
    "uri": "http://localhost:3000/documents/test",
    "permissions": {
      "delete": [
        "[email protected]"
      ],
      "update": [
        "[email protected]"
      ],
      "read": [
        "[email protected]"
      ]
    },
    "tags": [],
    "groups": [
      "demo",
      "2013"
    ]
}

EDITED: Corrected Model/Schema confusion, which wasn't in my code, but was left out in the copy/paste. Thanks.

4
  • Do you mean an empty array (permissions: []) or an array containing an empty string (permissions: [''])? Can you edit your question to include an example doc that's being returned that shouldn't? Commented Feb 22, 2013 at 21:24
  • @JohnnyHK done -- note the permissions subdoc schema, which is what I think I am not handling correctly -- it's a subdoc, with fields that are each an array of strings. The strings are what I want to test against the acceptable values. Thanks. Commented Feb 22, 2013 at 22:11
  • I assume you repoint MainSchema to reference the model instead of the schema? Other than that it all looks fine and unfortunately I'm not able to reproduce the problem with that doc. It's omitted from the results unless I set req.query.user = "[email protected]". Commented Feb 22, 2013 at 22:27
  • @JohnnyHK Yes, good question -- clarified the model/schema thing. Commented Feb 25, 2013 at 15:39

1 Answer 1

3

I have looked for and not found an example of a mongodb/mongoose query that tests either for a specified value in a subdocument array field or an empty array field.

Instead, since I control both the API (which this code comes from) and the client application, I refactored.

I now add one of three where clauses to the query, based on client-side user selection:

if (req.query.mode === 'user') {
    query.where('user').equals(req.query.user);
}
else if (req.query.mode === 'group') {
    query.where('groups').in(req.query.groups);
    query.$where('this.permissions.read.length === 0');
}
else if (req.query.mode === 'class') {
    query.$where('this.permissions.read.length === 0');
}

Thanks for your input, @JohnnyHK.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.