I can do this in MySQL:
WHERE 1 AND 1 AND 1
How can i repeat it in MongoDB? What is MongoDB's equivalent for WHERE 1 ?
UPDATE:
So. I don't know how choose best answer ^^ and expanded question. As #mark-hillick noticed - i'm searching the best way to build query.
Now I'm using this way (express+mongoose):
//req.query - get/post object in Express
for (var q in req.query) {
if (req.query[q]) { //simplified example
query[q] = req.query[q];
};
}
Collection.find(query)
Your suggestions?
There is a SQL-MongoDB Mapping Chart here that you will find useful.
It has a tonne of examples on what you do within MongoDB when you want to do the same operation as "WHERE" in MySQL. For example -
SELECT a,b FROM users WHERE age=33
is
db.users.find({age:33}, {a:1,b:1})
or
SELECT * FROM users WHERE a=1 and b=1
is
db.users.find({a:1,b:1})
MongoDB is document oriented database and documents in MongoDB consists key-value pairs. So, in MongoDB you can't run single value query as you did in MySql. Assuming you hold your data in the field name a, similar query in MongoDB could be like :
db.test.find({$and : [{a:1},{a:1}, {a:1}]});
If' you're trying to build query clauses, AND is implicit in Mongo. Therefore, if you have the following;
db.col.find({name:"dave"})
you could just add another;
db.col.find({name:"dave", age:33})
and so on.
Related
I'm developing an API using NestJS & TypeORM to fetch data from a MySQL DB. Currently I'm trying to get all the instances of an entity (HearingTonalTestPage) and all the related entities (e.g. Frequency). I can get it using createQueryBuilder:
const queryBuilder = await this.hearingTonalTestPageRepo
.createQueryBuilder('hearing_tonal_test_page')
.innerJoinAndSelect('hearing_tonal_test_page.hearingTest', 'hearingTest')
.innerJoinAndSelect('hearingTest.page', 'page')
.innerJoinAndSelect('hearing_tonal_test_page.frequencies', 'frequencies')
.innerJoinAndSelect('frequencies.frequency', 'frequency')
.where(whereConditions)
.orderBy(`page.${orderBy}`, StringToSortType(pageFilterDto.ascending));
The problem here is that this will produce a SQL query (screenshot below) which will output a line per each related entity (Frequency), when I want to output a line per each HearingTonalTestPage (in the screenshot example, 3 rows instead of 12) without losing its relations data. Reading the docs, apparently this can be easily achieved using the relations option with .find(). With QueryBuilder I see some relation methods, but from I've read, under the hood it will produce JOINs, which of course I want to avoid.
So the 1 million $ question here is: is it possible with CreateQueryBuilder to load the relations after querying the main entities (something similar to .find({ relations: { } }) )? If yes, how can I achieve it?
I am not an expert, but I had a similar case and using:
const qb = this.createQueryBuilder("product");
// apply relations
FindOptionsUtils.applyRelationsRecursively(qb, ["createdBy", "updatedBy"], qb.alias, this.metadata, "");
return qb
.orderBy("product.id", "DESC")
.limit(1)
.getOne();
it worked for me, all relations are correctly loaded.
ref: https://github.com/typeorm/typeorm/blob/master/src/find-options/FindOptionsUtils.ts
You say that you want to avoid JOINs, and are seeking an analogue of find({relations: {}}), but, as the documentation says, find({relations: {}}) uses under the hood, expectedly, LEFT JOINs. So when we talk about query with relations, it can't be without JOIN's.
Now about the problem:
The problem here is that this will produce a SQL query (screenshot
below) which will output a line per each related entity (Frequency),
when I want to output a line per each HearingTonalTestPage
Your query looks fine. And the result of the query, also, is ok. I think that you expected to have as a result of the query something similar to json structure(when the relation field contains all the information inside itself instead of creating new rows and spread all its values on several rows). But that is how the SQL works. By the way, getMany() method should return 3 HearingTonalTestPage objects, not 12, so what the SQL query returns should not worry you.
The main question:
is it possible with CreateQueryBuilder to load the relations after
querying the main entities
I did't get what do you mean by saying "after querying the main entities". Can you provide more context?
I have an entity, let's call it Foo and a second one Bar
Foo can (but doesn't have to) have one or multiple Bar entries assigned. It looks something like this:
/**
* #ORM\OneToMany(targetEntity="Bar", mappedBy="foo")
* #ORM\OrderBy({"name" = "ASC"})
*/
private $bars;
I now would like to load in one case only Foo entities that have at least one Bar entity assigned. Previously, there was one foreach loop to traverse all Foo entries and if it had assigned entries, the Foo entry got assigned to an array.
My current implementation is in the FooRepository a function called findIfTheresBar which looks like this:
$qb = $this->_em->createQueryBuilder()
->select('e')
->from($this->_entityName, 'e')
/* some where stuff here */
->addOrderBy('e.name', 'ASC')
->join('e.bars', 'b')
->groupBy('e.id');
Is this the correct way to load such entries? Is there a better (faster) way? It kind of feels as if it should have a having(...) in the query.
EDIT:
I've investigated it a little further. The query should return 373 out of 437 entries.
Version 1: only using join(), this loaded 373 entries in 7.88ms
Version 2: using join() and having(), this loaded 373 entries in 8.91ms
Version 3: only using leftJoin(), this loaded all 437 entries (which isn't desired) in 8.05ms
Version 4: using leftJoin() and having(), this loaded 373 entries in 8.14ms
Since Version 1 which only uses an innerJoin as #Chausser pointed out, is the fastest, I will stick to that one.
Note: I'm not saying Version 1 will be the fastest in all scenarios and on every hardware, so kind of a follow up question, does anybody know about a performance comparison?
Please take a look at this answer for more information on how SQL JOINs work: https://stackoverflow.com/a/16598900/1307183
Using a join, which is an alias of innerJoin, is exactly what you want. This only returns records where entries exist in both Foo and Bar - aka where the association/attached entity exists. This calls INNER JOIN in SQL, which, if your database structure is defined correctly, is the absolute best and fastest way to get the data you want.
Using a leftJoin calls LEFT JOIN in SQL, which returns all records from Foo, even if there is no Bar associated with it (for example, where bar_id in your foo table would be null).
You have no reason to use having() in any of the above scenarios you described. If you want to filter further you would do that with a ->addWhere() function. Using the having() clause is something you would only want to do if you were selecting aggregate data in your original query (like SELECT SUM(field) AS sum_field).
Is it possible to create a generic query that would work for different types of documents? For example I have "cases" and "factories",
They have different set of fields. e.g:
{
id: 'case_o1',
name: 'Case numero uno',
amount: 40
}
{
id: 'factory_002',
location: 'Venezuela',
workers: 200,
operating: true
}
Is it possible to create a generic query where I would pass the type of an entity (case or factory) and additional parameters and it would filter results based on those?
I could of course use javascript view, but it doesn't allow me to filter by multiple fields. Let's say I want to fetch all factories located in Venezuela, with number of workers between 20 and 55.
I started with this, but then I got stuck:
select * from `mybucket` as entity
where position(meta(entity).id, $entity_type) == 0
How do I pass multiple predicates and have the query to recognize them?
I can of course list fields like this:
where position(meta(entity).id, $entity_type) == 0
and entity.location == 'Venezuela'
and entity.workers > $workers_min
and entity.workers < $workers_max
but then
I'm gonna have to create a separate query for each entity
And even then it won't solve my problem - I have no idea how to ignore predicates, what if next time $workers_min and $workers_max are not passed, does it mean I have to create a query for every single predicate (column)?
For security reasons I cannot generate free-form queries and pass them to Couchbase server, all the queries are already stored in the database, our api just picks them up out of a document and executes them
I think it's possible to create a query that would be "short-circuiting" for args that's undefined (e.g. WHERE $location IS MISSING OR entity.location == $location or something like that)
Is it possible at all to create a query that would be able to effectively filter and order a dataset based on arbitrary parameters? Or there's no way?
#Agzam. Sorry. I were writting my comment when you said it. But anyway. What you are asking for is possible by using coalesces in a not too complex expressions, but it is a REALLY bad idea because this will drastically throw down most of internal database optimizations. Including the use of any existing index. So, except if you are dealing with a relatively small database (and you are sure it will remain being approximately the same size), I suggest you to better try distinct approach… This is, in fact, the reason I implmented sqlapi.
If you need to have all querys previously stored in database, it probably could be much better to sort given arguments by its name and precalculate and store precalculated querys for each possible combination.
You can do it by assigning a default value to the variable when is not used. For instance if $location is not used you can set it to -1 as default value.
Then the where condition would be:
WHERE ($location=-1 OR entity.location = $location)
I have the following SELECT statement for SQL.
SELECT TransAmount FROM STOCK WHERE TransAmount between 100 and 110;
However, this statement generates an error from querymongo.com. It says "Failure parsing MySQL query: Unable to parse WHERE clause due to unrecognized operator ". I assume it is talking about the between clause.
Correct me if I'm wrong, but does this SQL statement do the exact same thing as the one above?
SELECT TransAmount FROM STOCK WHERE TransAmount > 100 and TransAmount < 110;
This statement generates the following MongoDB code.
db.STOCK.find({
"TransAmount": {
"$gt": 100,
"$lt": 110
}
}, {
"TransAmount": 1
});
It looks like MongoDB doesn't have a 'between' operator. Does MongoDB handle selection within ranges with a different keyword, or do you have to set it up like so $gt/%lt?
Between is just a shortcut (a sort of symlink) to your second query, I guess it makes life easier.
MongoDB has not yet implemented such a shortcut, I have looked around a bit for a JIRA declaring someone wants such an operator however, no luck.
The one and only way of doing ranges in MongoDB is to use $gt and $lt (you could count $in etc but that is a different kind of range, not what your looking for).
How do I convert the following into MongoDB query ?
sets_progress = Photo.select('count(status) as count, status, photoset_id')
.where('photoset_id IN (?)', sets_tracked_array)
.group('photoset_id, status')
There is no 1 to 1 mapping of a SQL query to a NoSQL implementation. You'll need to precalculate your data to match the way you want to access that data.
If it is small enough, then this query will need to change into a map-reduce job. More here: http://www.mongodb.org/display/DOCS/MapReduce
Here's a decent tutorial that takes a query that GROUP's and converts to map-reduce: http://www.mongovue.com/2010/11/03/yet-another-mongodb-map-reduce-tutorial/