Multiple Patterns for Regex Expression MongoDB Atlas - mongodb-query

I'm trying to get a MongoDB aggregate pipeline working. I need to match name to "John" and regex their "hometown" field to regex matching "CAPETOWN" or "FLORIDA" without case sensitivity. Basically if either of those patterns exist in the "hometown" field of the document. This is what I have so far.
const news = await cachedDb.collection(COLLECTION).aggregate([
{ $match: { "name": "John", "hometown": { $regex: /CAPETOWN/, $options: 'i' } } }
]).toArray();
I can only get CAPETOWN included, I want an OR. Such as "hometown" contains CAPETOWN OR FLORIDA with option (i). How do I go about doing that?

You have to set OR directly in the regex string.
db.collection.aggregate([
{
$match: {
"hometown": {
$regex: "CAPETOWN|FLORIDA",
$options: "i"
}
}
}
])
You can test it here

Related

Mongodb Query to filter documents based on the length of a field value

I am writing a AWS lambda code in python. My database is AWS DocumentDB. And I use pymongo.
This code snippet works fine
query = {"media_id": {"$exists": True}} collection.find(query)
But it returns a lot of records, so I want to fetch the records where the length of media_id field is less than 3.
For that I tried this query query = { "media_id": {"$exists": True}, "$expr: {"$lt": [{"$strLenCP": "$media_id"}, 3]},}, but I get
Feature not supported Error
because $expr is not supported in DocumentDB.
I am looking for the query which works in DocumentDB.
The solution might seems a bit tedious, but all the operations inside should be supported according to the official doc
Use an aggregation to project an auxiliary field to store the length of media_id then match on your criteria.
db.collection.aggregate([
{
$addFields: {
length: {
"$strLenCP": "$media_id"
}
}
},
{
$match: {
media_id: {
$exists: true
},
length: {
$gte: 3
}
}
},
{
"$project": {
length: false
}
}
])

MarkLogic - Xpath on JSON document

MarkLogic Version: 9.0-6.2
I am trying to apply Xpath in extract-document-data (using Query Options) on a JSON document shown below. I need to filter out "Channel" property if the underneath property "OptIn" has a value of "True".
{
"Category":
{
"Name": "Severe Weather",
"Channels":[
{
"Channel":
{
"Name":"Email",
"OptIn": "True"
}
},
{
"Channel":
{
"Name":"Text",
"OptIn": "False"
}
}
]
}
}
I tried below code,
'<extract-document-data selected="include">' +
'<extract-path>//*[OptIn="True"]/../..</extract-path>' +
'</extract-document-data>' +
which is only pulling from "Channel" property as shown below.
[
{
"Channel": {
"Name": "Email",
"OptIn": "True"
}
}
]
But my need is to pull from parent "Category" property, but filter out the Channels that have OptIn value as False.
Any pointers?
If I understand correctly, you'd like to extract 'Category', but only with those 'Channel's that have 'OptIn' equalling 'true', right?
Extract-document-data is not advanced enough for that. You best extract entire Categories which have at least one OptIn equalling true (//Category[//OptIn = 'true']), and use a REST transform on the search response to trim down the unwanted Channels..
HTH!

SQL Server Replace in MongoDB

I want to do a replace in projection. Like a SQL Server REPLACE. I'm pretty sure we can handle that in code but looking for some shell commands.
Here is what I have
db.OrderHistoryHeader.aggregate([
{
$project:{
"_id":0,
"OrderNo":1 // I want to do Replace(OrderNo,'XYZ','ABC')
}
}
],
{
allowDiskUse:true
}).pretty();
There's no built-in operator for that currently but you can use $indexOfBytes combined with $substr and $concat.
db.OrderHistoryHeader.aggregate([
{
$addFields:
{
index: { $indexOfBytes: [ "$OrderNo", "XYZ" ] },
}
},
{
$project: {
OrderNo: {
$concat: [
{ $substr: [ "$OrderNo", 0, "$index" ] },
"ABC",
{ $substr: [ "$OrderNo", { $add: [3, "$index"] }, -1 ] }
]
}
}
},
{
$project: {
index: 0
}
}
])
Where 3 is the length of text being replaced.
You can use the replaceOne method
db.collection.replaceOne(filter, replacement, options)
From documentation:
Behavior
replaceOne() replaces the first matching document in the collection that matches the filter, using the replacement document.
upsert
If upsert: true and no documents match the filter, db.collection.replaceOne() creates a new document based on the replacement document.

Nest Elastic - Building Dynamic Nested Query

I have to query a nested object using Nest, however the query is built in dynamic way. Below is code that demonstrate using query on nested "books" in a static way
QueryContainer qry;
qry = new QueryStringQuery()
{
DefaultField = "name",
DefaultOperator = Operator.And,
Query = "salman"
};
QueryContainer qry1 = null;
qry1 = new RangeQuery() // used to search for range ( from , to)
{
Field = "modified",
GreaterThanOrEqualTo = Convert.ToDateTime("21/12/2015").ToString("dd/MM/yyyy"),
};
QueryContainer all = qry && qry1;
var results = elastic.Search<Document>(s => s
.Query(q => q
.Bool(qb => qb
.Must(all)))
.Filter(f =>
f.Nested(n => n
.Path("books")
.Filter(f3 => f3.And(
f1 => f1.Term("book.isbn", "122"),
f2 => f2.Term("book.author", "X"))
)
)
)
);
The problem is that i need to combine multiple queries (using And,OR operators) for "books" in dynamic fashion. For example, get the books that satisfy these set of conditions:
Condition 1: Books that has Author "X" and isbn "1"
Condition 2: Books that has Author "X" and isbn "2"
Condition 3: Books that has Author "Z" and isbn "3"
Other Condtions: .....
Now, the filter in the nested Query should retrieve books if:
Condition 1 AND Condition 2 Or Condition 3
Suppose that i have class name FilterOptions that contains the following attributes:
FieldName
Value
Operator (which will combine the next filter)
I am going to loop on the given FilterOptions array to build the query.
Question:
What should i use to build the nested query? Is it a FilterDesciptor and how to combine them add the nested query to the Search Method?
Please, recommend any valuable link or example?
I agree with paweloque, it seems your first two conditions are contradictory and wouldn't work if AND-ed together. Ignoring that, here's my solution. I've implemented this in such a way that allows for more than the three specific conditions you have. I too feel it would fit better in a bool statement.
QueryContainer andQuery = null;
QueryContainer orQuery = null;
foreach(var authorFilter in FilterOptions.Where(f=>f.Operator==Operator.And))
{
andQuery &= new TermQuery
{
Field = authorFilter.FieldName,
Value = authorFilter.Value
};
}
foreach(var authorFilter in FilterOptions.Where(f=>f.Operator==Operator.Or))
{
orQuery |= new TermQuery
{
Field = authorFilter.FieldName,
Value = authorFilter.Value
};
}
After that, in the .Nested call I would put:
.Path("books")
.Query(q=>q
.Bool(bq=>bq
.Must(m=>m.MatchAll() && andQuery)
.Should(orQuery)
))
In the specific case of the Condition 1 and Condition 2 you'd probably not get any results because these are exclusive conditions. But I assume now, that you want to get results which match either of those conditions. You've chosen nested which is definitely the way to go. With the nested type you can combine parameters for a single book.
Combining nested queries
For your use case I'd use bool query type with must or should clauses.
A query to get books for either Condition 1 or Condition 2 would be:
POST /books/_search
{
"query": {
"bool": {
"should": [
{
"nested": {
"path": "books",
"query": {
"bool": {
"must": [
{
"match": {
"books.isbn": "2"
}
},
{
"match": {
"books.author": "X"
}
}
]
}
}
}
},
{
"nested": {
"path": "books",
"query": {
"bool": {
"must": [
{
"match": {
"books.isbn": "1"
}
},
{
"match": {
"books.author": "X"
}
}
]
}
}
}
}
]
}
}
}
Can you explain, why are your books nested? Without nesting them in a top structure but indexing directly as a top level object in an index/type you could simplify your queries.
Not-Analyzed
There is another caveat that you have to remind: If you want to have an exact match on the author and the ISBN you have to make sure that the ISBN and author fields are set to not_analyzed. Otherwise they get analyzed and splitted into parts and your match would'n work very well.
E.g. if you have a ISBN Number with dashes, then it would get split into parts:
978-3-16-148410-0
would become indexed as:
978
3
16
148410
0
And a search with exactly the same ISBN number would give you all the books which have one of the sub-numbers in their ISBN number. If you want to prevent this, use the not_analyzed index-type and Multi-fields:
"isbn": {
"type": "string",
"fields": {
"raw": {
"type": "string",
"index": "not_analyzed"
}
}
}
Then to address the not_analyzed isbn field you'd have to call it:
books.isbn.raw
Hope this helps.

elastic search query filter out ids by wildcard

I'm hoping to create a query where it will filter out IDs containing a wildcard. For instance, I would like to search for something everywhere except where the ID contains the word current. Is this possible?
Yes it is possible using Regex Filter/Regex Query. I could not figure a way to directly do it using the Complement option hence I've used bool must_not to solve your problem for the time being. I'll refine the answer later if possible.
POST <index name>/_search
{
"query": {
"match_all": {}
},
"filter": {
"bool": {
"must_not": [
{
"regexp": {
"ID": {
"value": ".*current.*"
}
}
}
]
}
}
}