Search and return result in array - mongodb

I have a collection, like so:
post = {"topics":["japan","japanese","england","chinese"]}
post = {"topics":["canada","japan"]}
post = {"topics":["usa"]}
Now I want to implement a query can return a list of elements by checking the "topics" field to see if it contains a certain character.
For example, query for "ja", it returns all the tags start with "ja":
["japan", "japanese"],
query for "c", returns ["chinese","canada"]

You can not resolve this problem with a single query. You have to maintain either a global list or collection of all tags in order to search them efficiently using regular expressions or you need to iterate over all documents for matching the tags individually for each document.

Related

Query a Map containing a string saved in Cloud Firestore with Dart/Flutter

I've a problem with a query condition. I would like to query a string inside an array that's also inside a map. I manage to receive the information complet without the where condition but when I do this condition I've an error.
My function to retrieve all information
My array with a map inside
If category is the only value inside the produits array items, you can check for its existence with array-contains:
collectionRef.where('produits', arrayContains({ 'categorie': 'Alimentation' }))
If there are more properties for the array item, you will need to specify all of them in the call to arrayContains. There is no way to query for a subset of the array item.
In such cases, a common workaround is to add a extra array field to the document with just the property values you want to query for. For example:
produitsCategories: ['Alimentation', '...']
With that field in place you can then query it with:
collectionRef.where('produitsCategories', arrayContains('Alimentation'))

What is the effect of passing an array to Firestore database's orderBy clause?

Imagine the documents in my Firestore database represent recipes and each document has a 'tags' field, which is an array containing tags for that recipe (for example: spicy, French, vegetarian).
When a user searches for some tags, I use the array-contains-any operator to pull back all the recipes that contain at least one of the tags that a user has searched for. I then order the returned documents by the number of tags that have been matched. For example, if a user searched for 'spicy French vegetarian' then the documents whose 'tags' field contains all three of those tags appears first, followed by the documents whose 'tags' field contains two of those tags etc.
I am currently sorting manually using:
const userSearchedTags: string[] = [...] // array of tags searched for by user
recipes.sort((r1, r2) => {
const r1Tags: string[] = r1.get('tags');
const r2Tags: string[] = r2.get('tags');
const r1TagMatchQuant = r1Tags.filter(r1Tag => userSearchedTags.includes(r1Tag)).length;
const r2TagMatchQuant = r2Tags.filter(r2Tag => userSearchedTags.includes(r2Tag)).length;
return r2TagMatchQuant - r1TagMatchQuant;
});
But I want to paginate this query, which requires them to be ordered by Firebase so I thought I might be able to use the orderBy clause in my query so that Firebase gives me the documents back already ordered by number of matched tags. If this worked then I would be able to use the startAfter() clause to paginate my query. I attempted this:
FirebaseFirestore.instance
.collection('recipes')
.where('tags', arrayContainsAny: userSearchedTags)
.orderBy('tags')
.get();
But it does not return the documents ordered by number of matched tags. I thought it might be returning the documents ordered by the length of the 'tags' array on each document, but further testing has shown it is not doing that either. So my question is how is Firebase ordering these documents when I pass 'tags' (a field that is an array) to the orderBy clause?
Also, is there a way to get Firebase to return the documents to me ordered by the number of matched tags or is the way I am doing it now (manually sorting after pulling back all documents with at least one matching tag) the only way?
The order in which document are returned is typically the same as however the documents are present in the index that is being searched. Assuming your tags are strings, then according to the documentation on data types that'd be:
UTF-8 encoded byte order
So I expect it to be the order of that first tag that it matches, and definitely not the number of tags matched that you were hoping for. That type of results prioritization based on a calculated value is simply not something Firestore queries are made for as it would require that Firestore reorders the matches before returning them to you.

ODM Query Builder: Is there an "inverse in" operator, or equivalent operation?

In my synfony 2 project, I'm filtering search results using a query builder. In my MongoDB i have some values in an array.
Query Bulider has the "in" operator that allows to query for values that equal one of many in an array. I wanted to perform the opposite operation, i.e. given a single value, query for entries in the data base that contain an array, that contains my value.
For instance, say I have this entry in my MongoDB:
{
"_id": 123,
"name": "John",
"countries_visited":
[
"Australia"
"Bulgaria",
"Canada"
]
}
And I want to query my database for persons who have visited "Canada". Right now, I'm using the where attribute as follows, but I'm looking for a better way to do this.
$qb->field('countries_visited')->where("function(){
return this.indexOf(".$countryName.") > -1
}");
edit:
The in and notIn operator receives an array as parameter and compares it against a single value in MongoDB. I need to provide a single parameter and apply it to an array field in MongoDB, hence "inverse in". I guess I need a contains operator if there's such a thing.
Interesting, MongoDB takes care of this automatically. If querying for a single value against an array field, Mongo will assume you want the check the array if it contains the value.
Taken from the docs:
Match an Array Element
Equality matches can specify a single element in the array to match. These specifications match if the array contains at least one element with the specified value.
So you should be able to do
$users = $dm->getRepository('User')->findOneBy([
'countries_visited' => 'Canada'
]);
or
$qb->field('countries_visited')->equals('Canada');

In Mongodb, how to retrieve the subset of an object that matches a condition?

What I'm trying to do:
Filter a field of a collection that matches a given condition. Instead of returning every item in the field (which is an array of items), I only want to see matched items.
Similar to
select items from test where items.histPrices=[10,12]
It is also similar to what's found on the mongodb website here: http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields
Here's what I have been trying:
db.test.save({"name":"record", "items":[{"histPrices":[10,12],"name":"stuff"}]})
db.test.save({"name":"record", "items":[{"histPrices":[10,12],"name":"stuff"},
{"histPrices":[12,13],"name":"stuff"},{"histPrices":[11,14],"name":"stuff"}]})
db.test.find({},{"name":1,"items.histPrices":[10, 12]})
It will return all the objects that have a match for items.histPrices:[10,12], including ALL of the items in items[]. But I don't want the ones that don't match the condition.
From the comments left on Mongodb two years ago, the solution to get only the items with that histPrices[10,12] is to do it with javascript code, namely, loop through the result set and filter out the other items.
I wonder if there's a way to do that with just the query.
Your find query is wrong
db.test.find({},{"name":1,"items.histPrices":[10, 12]})
Your condition statement should be in the first part of the find statement.In your query {} means fetch all documents similar to this sql
select items from test (no where clause)
you have to change your mongodb find to
db.test.find({"items.histPrices":[10, 12]},{"name":1})
make it work
since your items is an array and if you wanted to return only the matching sub item, you have to use positional operator
db.test.find({"items.histPrices":[10, 12]},{"name":1,'items.$':1})
When working with arrays Embedded to the Document, the best approach is the one suggested by Chien-Wei Huang.
I would just add another aggregation, with the $group (in cases the document is very long, you may not want to retrieve all its content, only the array elements) Operator.
Now the command would look like:
db.test.aggregate({$match:{name:"record"}},
{$unwind:"$items"},
{$match {"items.histPrices":[10, 12]}},
{$group: {_id: "$_id",items: {$push: "$items"}}});)
If you are interested to return only one element from the array in each collection, then you should use projection instead
The same kind of issue solved here:
MongoDB Retrieve a subset of an array in a collection by specifying two fields which should match
db.test.aggregate({$unwind:"$items"}, {$match:{"items.histPrices":[10, 12]}})
But I don't know whether the performance would be OK. You have to verify it with your data.
The usage of $unwind
If you want add some filter condition like name="record", just add another $march at first, ex:
db.test.aggregate({$match:{name:"record"}}, {$unwind:"$items"}, {$match:{"items.histPrices":[10, 12]}})
https://jira.mongodb.org/browse/SERVER-828
Get particular element from mongoDB array
MongoDB query to retrieve one array value by a value in the array

Mongo - dot notation works like $or? Can't provide multiple parameters?

I tried to update an existing document with two dot notation parameters, my query:
{ _id: "4eda5...", comments._id: "4eda6...", comments.author: "john" }
my update was:
{ "comments.$.deleted": true }
However, weirdly enough, when I passed a non-existent combination of comment id+author, it just updated the first matching comment by that author.
Any ideas why that's happening?
EDIT: C# Code sample
var query = Query.And(Query.EQ("_id", itemId), Query.EQ("cmts._id", commentId));
if (!string.IsNullOrEmpty(author))
query = Query.And(query, Query.EQ("cmts.Author", author));
var update = Update.Set("cmts.$.deleted", true);
var result = myCol.Update(query, update, UpdateFlags.None, SafeMode.True);
You want $elemMatch if you want the _id and author to be in the same comment. Really, your query doesn't make much sense including the author as the id should be as unique as you can get, no?
It is based on the first matching array element which replaces the "$" in for the update.
This is working by design. It is similar to an or since it can find a document which both has the _id and an author that match in any of the array elements.
The query is not working the way you are expecting it to. Basically, when using the $ positional notation you need to make sure that your query only has one clause that queries an array, otherwise it is ambiguous which of the two array comparisons the $ should refer to.
In your case, you are asking for a document where:
The _id is equal to some value
The comments array contains some document where the _id is equal to some value
The comments array contains some document where the author is equal to some value
Nothing in your query says that 2. and 3. need to be satisfied by the same document.
So even though you are using a non-existent combination of comment._id and comment.author, your comment array does have at least one entry where the _id is equal to your search value and some other entry (just not the same one) where the author is equal to your search value.
Since the author was the last one checked, that's what set the value of the $, and that's why that array element got updated.