I have an existing MongoDB database that I want to use with Parse Server. Please note that the data in the database was not created by Parse. Here’s an example of the structure of a document in my theaters
collection:
{
"_id": "59a47286cfa9a3a73e51e72c",
"theaterId": 1000,
"location": {
"address": {
"street1": "340 W Market",
"city": "Bloomington",
"state": "MN",
"zipcode": "55425"
},
"geo": {
"type": "Point",
"coordinates": [-93.24565, 44.85466]
}
}
}
When I query the collection using ParseQuery
, I get results, and Parse seems to automatically map the _id
field to an objectId
. Here’s my query and the result:
Query:
const query = new Parse.Query("theaters");
const theaters = await query.find();
Result:
[
{
"theaterId": 1000,
"location": {
"address": {
"street1": "340 W Market",
"city": "Bloomington",
"state": "MN",
"zipcode": "55425"
},
"geo": {
"type": "Point",
"coordinates": [-93.24565, 44.85466]
}
},
"objectId": "59a47286cfa9a3a73e51e72c"
},
{
"theaterId": 1003,
"location": {
"address": {
"street1": "45235 Worth Ave.",
"city": "California",
"state": "MD",
"zipcode": "20619"
},
"geo": {
"type": "Point",
"coordinates": [-76.512016, 38.29697]
}
},
"objectId": "59a47286cfa9a3a73e51e72d"
}
]
Notice how Parse maps the _id
field from MongoDB to objectId
in the results.
However, when I try to fetch a single document by its objectId
using the following code:
let singleTheaterQuery = new Parse.Query("theaters");
let singleTheater = await singleTheaterQuery.get("59a47286cfa9a3a73e51e72d");
I get the following error:
No object found
What I understand so far:
-
The documents in my database were not created by Parse, so they lack the native
objectId
field -
Parse maps
_id
toobjectId
when returning results from afind
query. -
Despite this mapping, Parse doesn’t seem to handle
get
queries properly.
My question: If Parse can map _id
to objectId
during a find
query, why can’t it handle fetching a single object using the get
method? Is there a configuration or workaround to enable this behavior, or is this a limitation of Parse when working with existing MongoDB data?
The behaviour is a result of how Parse Server handles different query methods. This is what’s happening:
- The
.find()
method performs a raw MongoDB query and then transforms the results, including mapping_id
toobjectId
. - The
.get()
method, however, is optimized for Parse’s native data structure and expects the object to have been created through Parse (with a proper_id
field in Parse’s format).
You can work around this limitation by doing the following options:
- Use Query with
_id
Field: Instead of using.get()
, create a query that specifically looks for the_id
field using.equalTo()
as shown in the getTheaterById function above. - Use Native MongoDB Query: You can use Parse’s
withJSON()
method to write native MongoDB queries as demonstrated in thegetTheaterByIdNative
function. - Transform Your Data: If you’re planning to use this database long-term with Parse, you might want to consider migrating your data to follow Parse’s schema conventions.
The most straightforward immediate solution is to use the first approach. Here’s how you would use it:
const theater = await getTheaterById("59a47286cfa9a3a73e51e72d");
This should work because it queries the actual _id
field in MongoDB rather than trying to use Parse’s native object fetching mechanism.
The code snippets below are Parse Server MongoDB Query Solutions:
// Utility function to query by MongoDB _id
async function getTheaterById(theaterId) {
const query = new Parse.Query("theaters");
// Add a constraint to match the _id field
query.equalTo('_id', theaterId);
try {
const results = await query.first();
return results;
} catch (error) {
console.error('Error fetching theater:', error);
throw error;
}
}
// Alternative approach using MongoDB native query
async function getTheaterByIdNative(theaterId) {
const query = new Parse.Query("theaters");
// Use MongoDB's native query format
query.withJSON({
'_id': theaterId
});
try {
const results = await query.first();
return results;
} catch (error) {
console.error('Error fetching theater:', error);
throw error;
}
}
// Helper function to convert string ID to MongoDB ObjectId
function convertToObjectId(idString) {
const mongodb = require('mongodb');
try {
return new mongodb.ObjectId(idString);
} catch (error) {
console.error('Invalid ObjectId format:', error);
throw error;
}
}
// Example usage with both approaches
async function demonstrateQueries() {
try {
// Using the direct _id query
const theater1 = await getTheaterById("59a47286cfa9a3a73e51e72d");
console.log('Result using _id query:', theater1);
// Using native MongoDB query
const theater2 = await getTheaterByIdNative("59a47286cfa9a3a73e51e72d");
console.log('Result using native query:', theater2);
// If you need to convert string to ObjectId first
const objectId = convertToObjectId("59a47286cfa9a3a73e51e72d");
const theater3 = await getTheaterById(objectId);
console.log('Result using converted ObjectId:', theater3);
} catch (error) {
console.error('Error in demonstration:', error);
}
}
Hope this helps.
EDIT:
I’ve made some changes in the previous code samples:
// Correct way to query by MongoDB ObjectId
async function getTheaterById(theaterId) {
const query = new Parse.Query("theaters");
// Use matches query constraint with regex pattern for MongoDB ObjectId
query.matches('_id', '^' + theaterId + '$');
try {
const result = await query.first();
if (!result) {
throw new Error('Theater not found');
}
return result;
} catch (error) {
console.error('Error fetching theater:', error);
throw error;
}
}
// Example usage
async function fetchTheater() {
try {
const theaterId = "573a1392f29313caabcd9aab";
const theater = await getTheaterById(theaterId);
console.log('Found theater:', theater.toJSON());
} catch (error) {
console.error('Error:', error);
}
}
// Alternative approach using containedIn if you have multiple IDs
async function getTheatersByMultipleIds(theaterIds) {
const query = new Parse.Query("theaters");
// Use containedIn for multiple IDs
query.containedIn('_id', theaterIds);
try {
const results = await query.find();
return results;
} catch (error) {
console.error('Error fetching theaters:', error);
throw error;
}
}
// Example usage for multiple IDs
async function fetchMultipleTheaters() {
try {
const theaterIds = [
"573a1392f29313caabcd9aab",
"573a1392f29313caabcd9aac"
];
const theaters = await getTheatersByMultipleIds(theaterIds);
console.log('Found theaters:', theaters.map(t => t.toJSON()));
} catch (error) {
console.error('Error:', error);
}
}
Key changes here are:
-
The use of
matches()
with a regex pattern to match the exact ObjectId string instead of usingwithJSON()
or directequalTo()
-
The pattern
'^' + theaterId + '$'
will ensure an exact match of the ID (the^
matches the start of the string and$
matches the end)
You can use it like this:
const query = new Parse.Query("theaters");
const theaterId = "573a1392f29313caabcd9aab";
query.matches('_id', '^' + theaterId + '$');
try {
const theater = await query.first();
console.log(theater);
} catch (error) {
console.error("Error while fetching theater by ID:", error);
}
If you want to query multiple theaters by their IDs, use the containedIn
approach as shown in the getTheatersByMultipleIds
function in the modified code samples above.
This should work with your existing MongoDB data structure without requiring any modifications to the database.
Try this and see if it fixes the problem.
2